mirror of
https://github.com/zadam/trilium.git
synced 2025-11-02 03:16:11 +01:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a0de7312c | ||
|
|
b4b22d9353 | ||
|
|
d3eb640aa2 | ||
|
|
8ccc48c25d | ||
|
|
9a1a76605a | ||
|
|
a717ee00fb | ||
|
|
f5e27278ab | ||
|
|
20c24e26cc | ||
|
|
3bafc396fc | ||
|
|
3fa3e912a4 | ||
|
|
44219e7ccc | ||
|
|
5b67854cbe | ||
|
|
c6d912dcb7 | ||
|
|
da53c1eaa8 | ||
|
|
73bf2dcb02 | ||
|
|
719f10981e | ||
|
|
deb67d6275 | ||
|
|
e4039ea5e1 | ||
|
|
78a50be663 | ||
|
|
3d3ad3b99b | ||
|
|
0d9cdcac85 | ||
|
|
350331e2ef | ||
|
|
e8a9e49e9e | ||
|
|
fb55cdaea6 | ||
|
|
b9b2cc8364 | ||
|
|
8dfdd090f5 | ||
|
|
fe7705524a | ||
|
|
2b1b7774f8 | ||
|
|
2d58019d6e | ||
|
|
fe31f08c0d | ||
|
|
ad7a55d305 | ||
|
|
4ce4ac9584 | ||
|
|
88bd65c679 | ||
|
|
9eab3026bb | ||
|
|
7abaedbf31 | ||
|
|
402718d293 | ||
|
|
990a84c202 | ||
|
|
d8e181a828 | ||
|
|
adb8caa8a2 | ||
|
|
7651c53363 | ||
|
|
0f25c8a95f | ||
|
|
1a49894adf | ||
|
|
bd8c078fb9 | ||
|
|
6e060b87b8 | ||
|
|
2375b170ba | ||
|
|
828cce0d78 | ||
|
|
ab535bf147 | ||
|
|
1876664dfb | ||
|
|
1690248e24 | ||
|
|
cbeb8ea17e | ||
|
|
9a13edd490 | ||
|
|
c9113ae752 | ||
|
|
0ec11d29ba | ||
|
|
a6cd25071e | ||
|
|
20fdeee048 | ||
|
|
a79a063d17 | ||
|
|
5e91b1b5e0 | ||
|
|
7877443fb4 | ||
|
|
759e47bfcf | ||
|
|
67bdffb27b | ||
|
|
3386dace3b | ||
|
|
f3a2e2cbde | ||
|
|
4c7c3105e8 | ||
|
|
1cd2711097 | ||
|
|
54d89a9f47 | ||
|
|
1699646b39 | ||
|
|
94a0a31f17 | ||
|
|
10219fb9dd | ||
|
|
17b23d92ef | ||
|
|
14f3c783f2 | ||
|
|
c1d0a1e07b |
3
.idea/.gitignore
generated
vendored
3
.idea/.gitignore
generated
vendored
@@ -2,4 +2,5 @@
|
||||
/workspace.xml
|
||||
|
||||
# Datasource local storage ignored files
|
||||
/dataSources.local.xml
|
||||
/dataSources.local.xml
|
||||
/dataSources/
|
||||
|
||||
3
.idea/dataSources.xml
generated
3
.idea/dataSources.xml
generated
@@ -6,9 +6,6 @@
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/../trilium-data/document.db</jdbc-url>
|
||||
<driver-properties>
|
||||
<property name="enable_load_extension" value="true" />
|
||||
</driver-properties>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
@@ -2,7 +2,7 @@
|
||||
<dataSource name="document.db">
|
||||
<database-model serializer="dbm" dbms="SQLITE" family-id="SQLITE" format-version="4.17">
|
||||
<root id="1">
|
||||
<ServerVersion>3.16.1</ServerVersion>
|
||||
<ServerVersion>3.25.1</ServerVersion>
|
||||
</root>
|
||||
<schema id="2" parent="1" name="main">
|
||||
<Current>1</Current>
|
||||
@@ -111,529 +111,550 @@
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="35" parent="7" name="hash">
|
||||
<column id="35" parent="7" name="deleteId">
|
||||
<Position>10</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<DefaultExpression>NULL</DefaultExpression>
|
||||
</column>
|
||||
<column id="36" parent="7" name="hash">
|
||||
<Position>11</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<column id="36" parent="7" name="isInheritable">
|
||||
<Position>11</Position>
|
||||
<column id="37" parent="7" name="isInheritable">
|
||||
<Position>12</Position>
|
||||
<DataType>int|0s</DataType>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<index id="37" parent="7" name="sqlite_autoindex_attributes_1">
|
||||
<index id="38" parent="7" name="sqlite_autoindex_attributes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>attributeId</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="38" parent="7" name="IDX_attributes_noteId_index">
|
||||
<index id="39" parent="7" name="IDX_attributes_noteId_index">
|
||||
<ColNames>noteId</ColNames>
|
||||
</index>
|
||||
<index id="39" parent="7" name="IDX_attributes_name_value">
|
||||
<index id="40" parent="7" name="IDX_attributes_name_value">
|
||||
<ColNames>name
|
||||
value</ColNames>
|
||||
</index>
|
||||
<index id="40" parent="7" name="IDX_attributes_value_index">
|
||||
<index id="41" parent="7" name="IDX_attributes_value_index">
|
||||
<ColNames>value</ColNames>
|
||||
</index>
|
||||
<key id="41" parent="7">
|
||||
<key id="42" parent="7">
|
||||
<ColNames>attributeId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_attributes_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="42" parent="8" name="branchId">
|
||||
<column id="43" parent="8" name="branchId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="43" parent="8" name="noteId">
|
||||
<column id="44" parent="8" name="noteId">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="44" parent="8" name="parentNoteId">
|
||||
<column id="45" parent="8" name="parentNoteId">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="45" parent="8" name="notePosition">
|
||||
<column id="46" parent="8" name="notePosition">
|
||||
<Position>4</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="46" parent="8" name="prefix">
|
||||
<column id="47" parent="8" name="prefix">
|
||||
<Position>5</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="47" parent="8" name="isExpanded">
|
||||
<column id="48" parent="8" name="isExpanded">
|
||||
<Position>6</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="48" parent="8" name="isDeleted">
|
||||
<column id="49" parent="8" name="isDeleted">
|
||||
<Position>7</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="49" parent="8" name="utcDateModified">
|
||||
<column id="50" parent="8" name="deleteId">
|
||||
<Position>8</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>NULL</DefaultExpression>
|
||||
</column>
|
||||
<column id="50" parent="8" name="utcDateCreated">
|
||||
<column id="51" parent="8" name="utcDateModified">
|
||||
<Position>9</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="51" parent="8" name="hash">
|
||||
<column id="52" parent="8" name="utcDateCreated">
|
||||
<Position>10</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="53" parent="8" name="hash">
|
||||
<Position>11</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<index id="52" parent="8" name="sqlite_autoindex_branches_1">
|
||||
<index id="54" parent="8" name="sqlite_autoindex_branches_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>branchId</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="53" parent="8" name="IDX_branches_noteId_parentNoteId">
|
||||
<index id="55" parent="8" name="IDX_branches_noteId_parentNoteId">
|
||||
<ColNames>noteId
|
||||
parentNoteId</ColNames>
|
||||
</index>
|
||||
<index id="54" parent="8" name="IDX_branches_parentNoteId">
|
||||
<index id="56" parent="8" name="IDX_branches_parentNoteId">
|
||||
<ColNames>parentNoteId</ColNames>
|
||||
</index>
|
||||
<key id="55" parent="8">
|
||||
<key id="57" parent="8">
|
||||
<ColNames>branchId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="56" parent="9" name="noteId">
|
||||
<column id="58" parent="9" name="noteId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="57" parent="9" name="content">
|
||||
<column id="59" parent="9" name="content">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<DefaultExpression>NULL</DefaultExpression>
|
||||
</column>
|
||||
<column id="58" parent="9" name="hash">
|
||||
<column id="60" parent="9" name="hash">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<column id="59" parent="9" name="utcDateModified">
|
||||
<column id="61" parent="9" name="utcDateModified">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="60" parent="9" name="sqlite_autoindex_note_contents_1">
|
||||
<index id="62" parent="9" name="sqlite_autoindex_note_contents_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="61" parent="9">
|
||||
<key id="63" parent="9">
|
||||
<ColNames>noteId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_note_contents_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="62" parent="10" name="noteRevisionId">
|
||||
<column id="64" parent="10" name="noteRevisionId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="63" parent="10" name="content">
|
||||
<column id="65" parent="10" name="content">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="64" parent="10" name="hash">
|
||||
<column id="66" parent="10" name="hash">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>''</DefaultExpression>
|
||||
</column>
|
||||
<column id="65" parent="10" name="utcDateModified">
|
||||
<column id="67" parent="10" name="utcDateModified">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="66" parent="10" name="sqlite_autoindex_note_revision_contents_1">
|
||||
<index id="68" parent="10" name="sqlite_autoindex_note_revision_contents_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="67" parent="10">
|
||||
<key id="69" parent="10">
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_note_revision_contents_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="68" parent="11" name="noteRevisionId">
|
||||
<column id="70" parent="11" name="noteRevisionId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="69" parent="11" name="noteId">
|
||||
<column id="71" parent="11" name="noteId">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="70" parent="11" name="title">
|
||||
<column id="72" parent="11" name="title">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="71" parent="11" name="contentLength">
|
||||
<column id="73" parent="11" name="contentLength">
|
||||
<Position>4</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="72" parent="11" name="isErased">
|
||||
<column id="74" parent="11" name="isErased">
|
||||
<Position>5</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="73" parent="11" name="isProtected">
|
||||
<column id="75" parent="11" name="isProtected">
|
||||
<Position>6</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="74" parent="11" name="utcDateLastEdited">
|
||||
<column id="76" parent="11" name="utcDateLastEdited">
|
||||
<Position>7</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="75" parent="11" name="utcDateCreated">
|
||||
<column id="77" parent="11" name="utcDateCreated">
|
||||
<Position>8</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="76" parent="11" name="utcDateModified">
|
||||
<column id="78" parent="11" name="utcDateModified">
|
||||
<Position>9</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="77" parent="11" name="dateLastEdited">
|
||||
<column id="79" parent="11" name="dateLastEdited">
|
||||
<Position>10</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="78" parent="11" name="dateCreated">
|
||||
<column id="80" parent="11" name="dateCreated">
|
||||
<Position>11</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="79" parent="11" name="type">
|
||||
<column id="81" parent="11" name="type">
|
||||
<Position>12</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>''</DefaultExpression>
|
||||
</column>
|
||||
<column id="80" parent="11" name="mime">
|
||||
<column id="82" parent="11" name="mime">
|
||||
<Position>13</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>''</DefaultExpression>
|
||||
</column>
|
||||
<column id="81" parent="11" name="hash">
|
||||
<column id="83" parent="11" name="hash">
|
||||
<Position>14</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>''</DefaultExpression>
|
||||
</column>
|
||||
<index id="82" parent="11" name="sqlite_autoindex_note_revisions_1">
|
||||
<index id="84" parent="11" name="sqlite_autoindex_note_revisions_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="83" parent="11" name="IDX_note_revisions_noteId">
|
||||
<index id="85" parent="11" name="IDX_note_revisions_noteId">
|
||||
<ColNames>noteId</ColNames>
|
||||
</index>
|
||||
<index id="84" parent="11" name="IDX_note_revisions_utcDateLastEdited">
|
||||
<index id="86" parent="11" name="IDX_note_revisions_utcDateLastEdited">
|
||||
<ColNames>utcDateLastEdited</ColNames>
|
||||
</index>
|
||||
<index id="85" parent="11" name="IDX_note_revisions_utcDateCreated">
|
||||
<index id="87" parent="11" name="IDX_note_revisions_utcDateCreated">
|
||||
<ColNames>utcDateCreated</ColNames>
|
||||
</index>
|
||||
<index id="86" parent="11" name="IDX_note_revisions_dateLastEdited">
|
||||
<index id="88" parent="11" name="IDX_note_revisions_dateLastEdited">
|
||||
<ColNames>dateLastEdited</ColNames>
|
||||
</index>
|
||||
<index id="87" parent="11" name="IDX_note_revisions_dateCreated">
|
||||
<index id="89" parent="11" name="IDX_note_revisions_dateCreated">
|
||||
<ColNames>dateCreated</ColNames>
|
||||
</index>
|
||||
<key id="88" parent="11">
|
||||
<key id="90" parent="11">
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="89" parent="12" name="noteId">
|
||||
<column id="91" parent="12" name="noteId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="90" parent="12" name="title">
|
||||
<column id="92" parent="12" name="title">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>"note"</DefaultExpression>
|
||||
</column>
|
||||
<column id="91" parent="12" name="contentLength">
|
||||
<column id="93" parent="12" name="contentLength">
|
||||
<Position>3</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="92" parent="12" name="isProtected">
|
||||
<column id="94" parent="12" name="isProtected">
|
||||
<Position>4</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="93" parent="12" name="type">
|
||||
<column id="95" parent="12" name="type">
|
||||
<Position>5</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>'text'</DefaultExpression>
|
||||
</column>
|
||||
<column id="94" parent="12" name="mime">
|
||||
<column id="96" parent="12" name="mime">
|
||||
<Position>6</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>'text/html'</DefaultExpression>
|
||||
</column>
|
||||
<column id="95" parent="12" name="hash">
|
||||
<column id="97" parent="12" name="hash">
|
||||
<Position>7</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<column id="96" parent="12" name="isDeleted">
|
||||
<column id="98" parent="12" name="isDeleted">
|
||||
<Position>8</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="97" parent="12" name="isErased">
|
||||
<column id="99" parent="12" name="deleteId">
|
||||
<Position>9</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<DefaultExpression>NULL</DefaultExpression>
|
||||
</column>
|
||||
<column id="100" parent="12" name="isErased">
|
||||
<Position>10</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="98" parent="12" name="dateCreated">
|
||||
<Position>10</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="99" parent="12" name="dateModified">
|
||||
<column id="101" parent="12" name="dateCreated">
|
||||
<Position>11</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="100" parent="12" name="utcDateCreated">
|
||||
<column id="102" parent="12" name="dateModified">
|
||||
<Position>12</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="101" parent="12" name="utcDateModified">
|
||||
<column id="103" parent="12" name="utcDateCreated">
|
||||
<Position>13</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="102" parent="12" name="sqlite_autoindex_notes_1">
|
||||
<column id="104" parent="12" name="utcDateModified">
|
||||
<Position>14</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="105" parent="12" name="sqlite_autoindex_notes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="103" parent="12" name="IDX_notes_title">
|
||||
<index id="106" parent="12" name="IDX_notes_title">
|
||||
<ColNames>title</ColNames>
|
||||
</index>
|
||||
<index id="104" parent="12" name="IDX_notes_type">
|
||||
<index id="107" parent="12" name="IDX_notes_type">
|
||||
<ColNames>type</ColNames>
|
||||
</index>
|
||||
<index id="105" parent="12" name="IDX_notes_isDeleted">
|
||||
<index id="108" parent="12" name="IDX_notes_isDeleted">
|
||||
<ColNames>isDeleted</ColNames>
|
||||
</index>
|
||||
<index id="106" parent="12" name="IDX_notes_dateCreated">
|
||||
<index id="109" parent="12" name="IDX_notes_dateCreated">
|
||||
<ColNames>dateCreated</ColNames>
|
||||
</index>
|
||||
<index id="107" parent="12" name="IDX_notes_dateModified">
|
||||
<index id="110" parent="12" name="IDX_notes_dateModified">
|
||||
<ColNames>dateModified</ColNames>
|
||||
</index>
|
||||
<index id="108" parent="12" name="IDX_notes_utcDateCreated">
|
||||
<index id="111" parent="12" name="IDX_notes_utcDateCreated">
|
||||
<ColNames>utcDateCreated</ColNames>
|
||||
</index>
|
||||
<index id="109" parent="12" name="IDX_notes_utcDateModified">
|
||||
<index id="112" parent="12" name="IDX_notes_utcDateModified">
|
||||
<ColNames>utcDateModified</ColNames>
|
||||
</index>
|
||||
<key id="110" parent="12">
|
||||
<key id="113" parent="12">
|
||||
<ColNames>noteId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="111" parent="13" name="name">
|
||||
<column id="114" parent="13" name="name">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="112" parent="13" name="value">
|
||||
<column id="115" parent="13" name="value">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="113" parent="13" name="isSynced">
|
||||
<column id="116" parent="13" name="isSynced">
|
||||
<Position>3</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="114" parent="13" name="hash">
|
||||
<column id="117" parent="13" name="hash">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<column id="115" parent="13" name="utcDateCreated">
|
||||
<column id="118" parent="13" name="utcDateCreated">
|
||||
<Position>5</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="116" parent="13" name="utcDateModified">
|
||||
<column id="119" parent="13" name="utcDateModified">
|
||||
<Position>6</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="117" parent="13" name="sqlite_autoindex_options_1">
|
||||
<index id="120" parent="13" name="sqlite_autoindex_options_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>name</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="118" parent="13">
|
||||
<key id="121" parent="13">
|
||||
<ColNames>name</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="119" parent="14" name="noteId">
|
||||
<column id="122" parent="14" name="noteId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="120" parent="14" name="notePath">
|
||||
<column id="123" parent="14" name="notePath">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="121" parent="14" name="hash">
|
||||
<column id="124" parent="14" name="hash">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<column id="122" parent="14" name="utcDateCreated">
|
||||
<column id="125" parent="14" name="utcDateCreated">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="123" parent="14" name="isDeleted">
|
||||
<column id="126" parent="14" name="isDeleted">
|
||||
<Position>5</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
</column>
|
||||
<index id="124" parent="14" name="sqlite_autoindex_recent_notes_1">
|
||||
<index id="127" parent="14" name="sqlite_autoindex_recent_notes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="125" parent="14">
|
||||
<key id="128" parent="14">
|
||||
<ColNames>noteId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="126" parent="15" name="sourceId">
|
||||
<column id="129" parent="15" name="sourceId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="127" parent="15" name="utcDateCreated">
|
||||
<column id="130" parent="15" name="utcDateCreated">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="128" parent="15" name="sqlite_autoindex_source_ids_1">
|
||||
<index id="131" parent="15" name="sqlite_autoindex_source_ids_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>sourceId</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="129" parent="15" name="IDX_source_ids_utcDateCreated">
|
||||
<index id="132" parent="15" name="IDX_source_ids_utcDateCreated">
|
||||
<ColNames>utcDateCreated</ColNames>
|
||||
</index>
|
||||
<key id="130" parent="15">
|
||||
<key id="133" parent="15">
|
||||
<ColNames>sourceId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="131" parent="16" name="type">
|
||||
<column id="134" parent="16" name="type">
|
||||
<Position>1</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="132" parent="16" name="name">
|
||||
<column id="135" parent="16" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="133" parent="16" name="tbl_name">
|
||||
<column id="136" parent="16" name="tbl_name">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="134" parent="16" name="rootpage">
|
||||
<column id="137" parent="16" name="rootpage">
|
||||
<Position>4</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
<DataType>int|0s</DataType>
|
||||
</column>
|
||||
<column id="135" parent="16" name="sql">
|
||||
<column id="138" parent="16" name="sql">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="136" parent="17" name="name">
|
||||
<column id="139" parent="17" name="name">
|
||||
<Position>1</Position>
|
||||
</column>
|
||||
<column id="137" parent="17" name="seq">
|
||||
<column id="140" parent="17" name="seq">
|
||||
<Position>2</Position>
|
||||
</column>
|
||||
<column id="138" parent="18" name="id">
|
||||
<column id="141" parent="18" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="139" parent="18" name="entityName">
|
||||
<column id="142" parent="18" name="entityName">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="140" parent="18" name="entityId">
|
||||
<column id="143" parent="18" name="entityId">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="141" parent="18" name="sourceId">
|
||||
<column id="144" parent="18" name="sourceId">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="142" parent="18" name="utcSyncDate">
|
||||
<column id="145" parent="18" name="isSynced">
|
||||
<Position>5</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="146" parent="18" name="utcSyncDate">
|
||||
<Position>6</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="143" parent="18" name="IDX_sync_entityName_entityId">
|
||||
<index id="147" parent="18" name="IDX_sync_entityName_entityId">
|
||||
<ColNames>entityName
|
||||
entityId</ColNames>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="144" parent="18" name="IDX_sync_utcSyncDate">
|
||||
<index id="148" parent="18" name="IDX_sync_utcSyncDate">
|
||||
<ColNames>utcSyncDate</ColNames>
|
||||
</index>
|
||||
<key id="145" parent="18">
|
||||
<key id="149" parent="18">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
|
||||
81
db/migrations/0156__add_deleteId.sql
Normal file
81
db/migrations/0156__add_deleteId.sql
Normal file
@@ -0,0 +1,81 @@
|
||||
CREATE TABLE IF NOT EXISTS "notes_mig" (
|
||||
`noteId` TEXT NOT NULL,
|
||||
`title` TEXT NOT NULL DEFAULT "note",
|
||||
`contentLength` INT NOT NULL,
|
||||
`isProtected` INT NOT NULL DEFAULT 0,
|
||||
`type` TEXT NOT NULL DEFAULT 'text',
|
||||
`mime` TEXT NOT NULL DEFAULT 'text/html',
|
||||
`hash` TEXT DEFAULT "" NOT NULL,
|
||||
`isDeleted` INT NOT NULL DEFAULT 0,
|
||||
`deleteId` TEXT DEFAULT NULL,
|
||||
`isErased` INT NOT NULL DEFAULT 0,
|
||||
`dateCreated` TEXT NOT NULL,
|
||||
`dateModified` TEXT NOT NULL,
|
||||
`utcDateCreated` TEXT NOT NULL,
|
||||
`utcDateModified` TEXT NOT NULL,
|
||||
PRIMARY KEY(`noteId`));
|
||||
|
||||
INSERT INTO notes_mig (noteId, title, contentLength, isProtected, type, mime, hash, isDeleted, isErased, dateCreated, dateModified, utcDateCreated, utcDateModified)
|
||||
SELECT noteId, title, -1, isProtected, type, mime, hash, isDeleted, isErased, dateCreated, dateModified, utcDateCreated, utcDateModified FROM notes;
|
||||
|
||||
DROP TABLE notes;
|
||||
ALTER TABLE notes_mig RENAME TO notes;
|
||||
|
||||
CREATE INDEX `IDX_notes_isDeleted` ON `notes` (`isDeleted`);
|
||||
CREATE INDEX `IDX_notes_title` ON `notes` (`title`);
|
||||
CREATE INDEX `IDX_notes_type` ON `notes` (`type`);
|
||||
CREATE INDEX `IDX_notes_dateCreated` ON `notes` (`dateCreated`);
|
||||
CREATE INDEX `IDX_notes_dateModified` ON `notes` (`dateModified`);
|
||||
CREATE INDEX `IDX_notes_utcDateModified` ON `notes` (`utcDateModified`);
|
||||
CREATE INDEX `IDX_notes_utcDateCreated` ON `notes` (`utcDateCreated`);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "branches_mig" (
|
||||
`branchId` TEXT NOT NULL,
|
||||
`noteId` TEXT NOT NULL,
|
||||
`parentNoteId` TEXT NOT NULL,
|
||||
`notePosition` INTEGER NOT NULL,
|
||||
`prefix` TEXT,
|
||||
`isExpanded` INTEGER NOT NULL DEFAULT 0,
|
||||
`isDeleted` INTEGER NOT NULL DEFAULT 0,
|
||||
`deleteId` TEXT DEFAULT NULL,
|
||||
`utcDateModified` TEXT NOT NULL,
|
||||
utcDateCreated TEXT NOT NULL,
|
||||
hash TEXT DEFAULT "" NOT NULL,
|
||||
PRIMARY KEY(`branchId`));
|
||||
|
||||
INSERT INTO branches_mig (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, utcDateModified, utcDateCreated, hash)
|
||||
SELECT branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, utcDateModified, utcDateCreated, hash FROM branches;
|
||||
|
||||
DROP TABLE branches;
|
||||
ALTER TABLE branches_mig RENAME TO branches;
|
||||
|
||||
CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`);
|
||||
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "attributes_mig"
|
||||
(
|
||||
attributeId TEXT not null primary key,
|
||||
noteId TEXT not null,
|
||||
type TEXT not null,
|
||||
name TEXT not null,
|
||||
value TEXT default '' not null,
|
||||
position INT default 0 not null,
|
||||
utcDateCreated TEXT not null,
|
||||
utcDateModified TEXT not null,
|
||||
isDeleted INT not null,
|
||||
`deleteId` TEXT DEFAULT NULL,
|
||||
hash TEXT default "" not null,
|
||||
isInheritable int DEFAULT 0 NULL);
|
||||
|
||||
INSERT INTO attributes_mig (attributeId, noteId, type, name, value, position, utcDateCreated, utcDateModified, isDeleted, hash, isInheritable)
|
||||
SELECT attributeId, noteId, type, name, value, position, utcDateCreated, utcDateModified, isDeleted, hash, isInheritable FROM attributes;
|
||||
|
||||
DROP TABLE attributes;
|
||||
ALTER TABLE attributes_mig RENAME TO attributes;
|
||||
|
||||
CREATE INDEX IDX_attributes_name_value
|
||||
on attributes (name, value);
|
||||
CREATE INDEX IDX_attributes_noteId_index
|
||||
on attributes (noteId);
|
||||
CREATE INDEX IDX_attributes_value_index
|
||||
on attributes (value);
|
||||
1
db/migrations/0157__fix_contentLength.sql
Normal file
1
db/migrations/0157__fix_contentLength.sql
Normal file
@@ -0,0 +1 @@
|
||||
UPDATE notes SET contentLength = COALESCE((SELECT COALESCE(LENGTH(content), 0) FROM note_contents WHERE note_contents.noteId = notes.noteId), -1);
|
||||
22
db/migrations/0158__add_isSynced_to_sync.sql
Normal file
22
db/migrations/0158__add_isSynced_to_sync.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
CREATE TABLE IF NOT EXISTS "sync_mig" (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`entityName` TEXT NOT NULL,
|
||||
`entityId` TEXT NOT NULL,
|
||||
`sourceId` TEXT NOT NULL,
|
||||
`isSynced` INTEGER default 0 not null,
|
||||
`utcSyncDate` TEXT NOT NULL);
|
||||
|
||||
INSERT INTO sync_mig (id, entityName, entityId, sourceId, isSynced, utcSyncDate)
|
||||
SELECT id, entityName, entityId, sourceId, 1, utcSyncDate FROM sync;
|
||||
|
||||
DROP TABLE sync;
|
||||
|
||||
ALTER TABLE sync_mig RENAME TO sync;
|
||||
|
||||
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
|
||||
`entityName`,
|
||||
`entityId`
|
||||
);
|
||||
CREATE INDEX `IDX_sync_utcSyncDate` ON `sync` (
|
||||
`utcSyncDate`
|
||||
);
|
||||
144
db/schema.sql
144
db/schema.sql
@@ -1,16 +1,8 @@
|
||||
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,
|
||||
`utcSyncDate` TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS "source_ids" (
|
||||
`sourceId` TEXT NOT NULL,
|
||||
`utcDateCreated` TEXT NOT NULL,
|
||||
PRIMARY KEY(`sourceId`)
|
||||
);
|
||||
CREATE INDEX IDX_source_ids_utcDateCreated
|
||||
on source_ids (utcDateCreated);
|
||||
CREATE TABLE IF NOT EXISTS "api_tokens"
|
||||
(
|
||||
apiTokenId TEXT PRIMARY KEY NOT NULL,
|
||||
@@ -27,32 +19,6 @@ CREATE TABLE IF NOT EXISTS "options"
|
||||
utcDateCreated TEXT not null,
|
||||
utcDateModified TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "attributes"
|
||||
(
|
||||
attributeId TEXT not null primary key,
|
||||
noteId TEXT not null,
|
||||
type TEXT not null,
|
||||
name TEXT not null,
|
||||
value TEXT default '' not null,
|
||||
position INT default 0 not null,
|
||||
utcDateCreated TEXT not null,
|
||||
utcDateModified TEXT not null,
|
||||
isDeleted INT not null,
|
||||
hash TEXT default "" not null,
|
||||
isInheritable int DEFAULT 0 NULL);
|
||||
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
|
||||
`entityName`,
|
||||
`entityId`
|
||||
);
|
||||
CREATE INDEX `IDX_sync_utcSyncDate` ON `sync` (
|
||||
`utcSyncDate`
|
||||
);
|
||||
CREATE INDEX IDX_attributes_name_value
|
||||
on attributes (name, value);
|
||||
CREATE INDEX IDX_attributes_noteId_index
|
||||
on attributes (noteId);
|
||||
CREATE INDEX IDX_attributes_value_index
|
||||
on attributes (value);
|
||||
CREATE TABLE IF NOT EXISTS "note_contents" (
|
||||
`noteId` TEXT NOT NULL,
|
||||
`content` TEXT NULL DEFAULT NULL,
|
||||
@@ -68,46 +34,10 @@ CREATE TABLE recent_notes
|
||||
utcDateCreated TEXT not null,
|
||||
isDeleted INT
|
||||
);
|
||||
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` INTEGER NOT NULL DEFAULT 0,
|
||||
`isDeleted` INTEGER NOT NULL DEFAULT 0,
|
||||
`utcDateModified` TEXT NOT NULL,
|
||||
utcDateCreated TEXT NOT NULL,
|
||||
hash TEXT DEFAULT "" NOT NULL,
|
||||
PRIMARY KEY(`branchId`));
|
||||
CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`);
|
||||
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
|
||||
CREATE TABLE IF NOT EXISTS "note_revision_contents" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY,
|
||||
`content` TEXT,
|
||||
hash TEXT DEFAULT '' NOT NULL,
|
||||
`utcDateModified` TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS "notes" (
|
||||
`noteId` TEXT NOT NULL,
|
||||
`title` TEXT NOT NULL DEFAULT "note",
|
||||
`contentLength` INT NOT NULL,
|
||||
`isProtected` INT NOT NULL DEFAULT 0,
|
||||
`type` TEXT NOT NULL DEFAULT 'text',
|
||||
`mime` TEXT NOT NULL DEFAULT 'text/html',
|
||||
`hash` TEXT DEFAULT "" NOT NULL,
|
||||
`isDeleted` INT NOT NULL DEFAULT 0,
|
||||
`isErased` INT NOT NULL DEFAULT 0,
|
||||
`dateCreated` TEXT NOT NULL,
|
||||
`dateModified` TEXT NOT NULL,
|
||||
`utcDateCreated` TEXT NOT NULL,
|
||||
`utcDateModified` TEXT NOT NULL,
|
||||
PRIMARY KEY(`noteId`));
|
||||
CREATE INDEX `IDX_notes_isDeleted` ON `notes` (`isDeleted`);
|
||||
CREATE INDEX `IDX_notes_title` ON `notes` (`title`);
|
||||
CREATE INDEX `IDX_notes_type` ON `notes` (`type`);
|
||||
CREATE INDEX `IDX_notes_dateCreated` ON `notes` (`dateCreated`);
|
||||
CREATE INDEX `IDX_notes_dateModified` ON `notes` (`dateModified`);
|
||||
CREATE INDEX `IDX_notes_utcDateModified` ON `notes` (`utcDateModified`);
|
||||
CREATE INDEX `IDX_notes_utcDateCreated` ON `notes` (`utcDateCreated`);
|
||||
CREATE TABLE IF NOT EXISTS "note_revisions" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY,
|
||||
`noteId` TEXT NOT NULL,
|
||||
`title` TEXT,
|
||||
@@ -127,3 +57,77 @@ CREATE INDEX `IDX_note_revisions_utcDateCreated` ON `note_revisions` (`utcDateCr
|
||||
CREATE INDEX `IDX_note_revisions_utcDateLastEdited` ON `note_revisions` (`utcDateLastEdited`);
|
||||
CREATE INDEX `IDX_note_revisions_dateCreated` ON `note_revisions` (`dateCreated`);
|
||||
CREATE INDEX `IDX_note_revisions_dateLastEdited` ON `note_revisions` (`dateLastEdited`);
|
||||
CREATE INDEX IDX_source_ids_utcDateCreated
|
||||
on source_ids (utcDateCreated);
|
||||
CREATE TABLE IF NOT EXISTS "notes" (
|
||||
`noteId` TEXT NOT NULL,
|
||||
`title` TEXT NOT NULL DEFAULT "note",
|
||||
`contentLength` INT NOT NULL,
|
||||
`isProtected` INT NOT NULL DEFAULT 0,
|
||||
`type` TEXT NOT NULL DEFAULT 'text',
|
||||
`mime` TEXT NOT NULL DEFAULT 'text/html',
|
||||
`hash` TEXT DEFAULT "" NOT NULL,
|
||||
`isDeleted` INT NOT NULL DEFAULT 0,
|
||||
`deleteId` TEXT DEFAULT NULL,
|
||||
`isErased` INT NOT NULL DEFAULT 0,
|
||||
`dateCreated` TEXT NOT NULL,
|
||||
`dateModified` TEXT NOT NULL,
|
||||
`utcDateCreated` TEXT NOT NULL,
|
||||
`utcDateModified` TEXT NOT NULL,
|
||||
PRIMARY KEY(`noteId`));
|
||||
CREATE INDEX `IDX_notes_isDeleted` ON `notes` (`isDeleted`);
|
||||
CREATE INDEX `IDX_notes_title` ON `notes` (`title`);
|
||||
CREATE INDEX `IDX_notes_type` ON `notes` (`type`);
|
||||
CREATE INDEX `IDX_notes_dateCreated` ON `notes` (`dateCreated`);
|
||||
CREATE INDEX `IDX_notes_dateModified` ON `notes` (`dateModified`);
|
||||
CREATE INDEX `IDX_notes_utcDateModified` ON `notes` (`utcDateModified`);
|
||||
CREATE INDEX `IDX_notes_utcDateCreated` ON `notes` (`utcDateCreated`);
|
||||
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` INTEGER NOT NULL DEFAULT 0,
|
||||
`isDeleted` INTEGER NOT NULL DEFAULT 0,
|
||||
`deleteId` TEXT DEFAULT NULL,
|
||||
`utcDateModified` TEXT NOT NULL,
|
||||
utcDateCreated TEXT NOT NULL,
|
||||
hash TEXT DEFAULT "" NOT NULL,
|
||||
PRIMARY KEY(`branchId`));
|
||||
CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`);
|
||||
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
|
||||
CREATE TABLE IF NOT EXISTS "attributes"
|
||||
(
|
||||
attributeId TEXT not null primary key,
|
||||
noteId TEXT not null,
|
||||
type TEXT not null,
|
||||
name TEXT not null,
|
||||
value TEXT default '' not null,
|
||||
position INT default 0 not null,
|
||||
utcDateCreated TEXT not null,
|
||||
utcDateModified TEXT not null,
|
||||
isDeleted INT not null,
|
||||
`deleteId` TEXT DEFAULT NULL,
|
||||
hash TEXT default "" not null,
|
||||
isInheritable int DEFAULT 0 NULL);
|
||||
CREATE INDEX IDX_attributes_name_value
|
||||
on attributes (name, value);
|
||||
CREATE INDEX IDX_attributes_noteId_index
|
||||
on attributes (noteId);
|
||||
CREATE INDEX IDX_attributes_value_index
|
||||
on attributes (value);
|
||||
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,
|
||||
`isSynced` INTEGER default 0 not null,
|
||||
`utcSyncDate` TEXT NOT NULL);
|
||||
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
|
||||
`entityName`,
|
||||
`entityId`
|
||||
);
|
||||
CREATE INDEX `IDX_sync_utcSyncDate` ON `sync` (
|
||||
`utcSyncDate`
|
||||
);
|
||||
|
||||
@@ -1240,6 +1240,143 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="addTextToActiveTabEditor"><span class="type-signature"></span>addTextToActiveTabEditor<span class="signature">(text)</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description">
|
||||
Adds given text to the editor cursor
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>text</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">string</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line305">line 305</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1366,7 +1503,7 @@
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line390">line 390</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line406">line 406</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -1785,7 +1922,7 @@
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line303">line 303</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line311">line 311</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -1891,7 +2028,7 @@
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line309">line 309</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line325">line 325</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -1949,6 +2086,119 @@
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="getActiveTabTextEditor"><span class="type-signature"></span>getActiveTabTextEditor<span class="signature">()</span><span class="type-signature"> → {Editor|null}</span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description">
|
||||
See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line319">line 319</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Returns:</h5>
|
||||
|
||||
|
||||
<div class="param-desc">
|
||||
CKEditor instance or null (e.g. if active note is not a text note)
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
Type
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<span class="param-type">Editor</span>
|
||||
|
|
||||
|
||||
<span class="param-type">null</span>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="getDateNote"><span class="type-signature"></span>getDateNote<span class="signature">(date)</span><span class="type-signature"> → {Promise.<<a href="NoteShort.html">NoteShort</a>>}</span></h4>
|
||||
|
||||
|
||||
@@ -2050,7 +2300,7 @@
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line356">line 356</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line372">line 372</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -2312,7 +2562,7 @@ if some action needs to happen on only one specific instance.
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line365">line 365</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line381">line 381</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -2775,7 +3025,7 @@ otherwise (by e.g. createNoteLink())
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line347">line 347</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line363">line 363</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -2930,7 +3180,7 @@ otherwise (by e.g. createNoteLink())
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line374">line 374</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line390">line 390</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -3039,7 +3289,7 @@ note.
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line320">line 320</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line336">line 336</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -3194,7 +3444,7 @@ note.
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line328">line 328</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line344">line 344</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -3433,7 +3683,7 @@ note.
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line339">line 339</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line355">line 355</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -4606,7 +4856,7 @@ Internally this serializes the anonymous function into string and sends it to ba
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line383">line 383</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line399">line 399</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -4757,7 +5007,7 @@ Internally this serializes the anonymous function into string and sends it to ba
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line334">line 334</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line350">line 350</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -5123,7 +5373,7 @@ Typical use case is when new note has been created, we should wait until it is s
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line401">line 401</a>
|
||||
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line417">line 417</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
@@ -324,12 +324,28 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
|
||||
*/
|
||||
this.createNoteLink = linkService.createNoteLink;
|
||||
|
||||
/**
|
||||
* Adds given text to the editor cursor
|
||||
*
|
||||
* @param {string} text
|
||||
* @method
|
||||
*/
|
||||
this.addTextToActiveTabEditor = linkService.addTextToEditor;
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @returns {NoteFull} active note (loaded into right pane)
|
||||
*/
|
||||
this.getActiveTabNote = noteDetailService.getActiveTabNote;
|
||||
|
||||
/**
|
||||
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
||||
*
|
||||
* @method
|
||||
* @returns {Editor|null} CKEditor instance or null (e.g. if active note is not a text note)
|
||||
*/
|
||||
this.getActiveTabTextEditor = noteDetailService.getActiveEditor;
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
||||
|
||||
@@ -26,9 +26,9 @@ app.on('ready', async () => {
|
||||
|
||||
await sqlInit.dbConnection;
|
||||
|
||||
// if schema doesn't exist -> setup process
|
||||
// if schema exists, then we need to wait until the migration process is finished
|
||||
if (await sqlInit.schemaExists()) {
|
||||
// if db is not initialized -> setup process
|
||||
// if db is initialized, then we need to wait until the migration process is finished
|
||||
if (await sqlInit.isDbInitialized()) {
|
||||
await sqlInit.dbReady;
|
||||
|
||||
await windowService.createMainWindow();
|
||||
|
||||
115
package-lock.json
generated
115
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trilium",
|
||||
"version": "0.39.3",
|
||||
"version": "0.40.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -552,6 +552,11 @@
|
||||
"defer-to-connect": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@tokenizer/token": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.1.1.tgz",
|
||||
"integrity": "sha512-XO6INPbZCxdprl+9qa/AAbFFOMzzwqYxpjPgLICrMD6C2FCw6qfJOPcBk6JqqPLSaZ/Qx87qn4rpPmPMwaAK6w=="
|
||||
},
|
||||
"@types/debug": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
|
||||
@@ -975,12 +980,11 @@
|
||||
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
|
||||
"integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.1.tgz",
|
||||
"integrity": "sha512-Yl+7nfreYKaLRvAvjNPkvfjnQHJM1yLBY3zhqAwcJSwR/6ETkanUgylgtIvkvz0xJ+p/vZuNw8X7Hnb7Whsbpw==",
|
||||
"requires": {
|
||||
"follow-redirects": "1.5.10",
|
||||
"is-buffer": "^2.0.2"
|
||||
"follow-redirects": "1.5.10"
|
||||
}
|
||||
},
|
||||
"bagpipe": {
|
||||
@@ -1771,13 +1775,13 @@
|
||||
}
|
||||
},
|
||||
"commonmark": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.29.0.tgz",
|
||||
"integrity": "sha512-Wc3kvAIm0EK85pHsM95Fev31wEN6/zQpwd2qcLDL8psjHRoUFvUeGHevIJAdToWUuFoX8WI/gmeDauqy32xgJQ==",
|
||||
"version": "0.29.1",
|
||||
"resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.29.1.tgz",
|
||||
"integrity": "sha512-DafPdNYFXoEhsSiR4O+dJ45UJBfDL4cBTks4B+agKiaWt7qjG0bIhg5xuCE0RqU71ikJcBIf4/sRHh9vYQVF8Q==",
|
||||
"requires": {
|
||||
"entities": "~ 1.1.1",
|
||||
"mdurl": "~ 1.0.1",
|
||||
"minimist": "~ 1.2.0",
|
||||
"entities": "~1.1.1",
|
||||
"mdurl": "~1.0.1",
|
||||
"minimist": "~1.2.0",
|
||||
"string.prototype.repeat": "^0.2.0"
|
||||
}
|
||||
},
|
||||
@@ -2081,9 +2085,9 @@
|
||||
"integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI="
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.8.18",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.18.tgz",
|
||||
"integrity": "sha512-JBMJZghNK8TtuoPnKNIzW9xavVVigld/zmZNpZSyQbkb2Opp55YIfZUpE4OEqPF/iyUVQTKcn1bC2HtC8B7s3g=="
|
||||
"version": "1.8.19",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.19.tgz",
|
||||
"integrity": "sha512-7kqOoj3oQSmqbvtvGFLU5iYqies+SqUiEGNT0UtUPPxcPYgY1BrkXR0Cq2R9HYSimBXN+xHkEN4Hi399W+Ovlg=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
@@ -3619,9 +3623,15 @@
|
||||
}
|
||||
},
|
||||
"file-type": {
|
||||
"version": "12.4.2",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz",
|
||||
"integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg=="
|
||||
"version": "13.0.3",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-13.0.3.tgz",
|
||||
"integrity": "sha512-fe3+9mnMLbpP8nRQm/cJgehypv375SOFOXFdLYyYe7hWxdWgS5hkDpWnld5At/vvfVEN6rVqKA3EOKMP03tJGg==",
|
||||
"requires": {
|
||||
"readable-web-to-node-stream": "^2.0.0",
|
||||
"strtok3": "^5.0.1",
|
||||
"token-types": "^2.0.0",
|
||||
"typedarray-to-buffer": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"filename-regex": {
|
||||
"version": "2.0.1",
|
||||
@@ -4772,6 +4782,13 @@
|
||||
"make-dir": "^3.0.0",
|
||||
"p-pipe": "^3.0.0",
|
||||
"replace-ext": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"file-type": {
|
||||
"version": "12.4.2",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-12.4.2.tgz",
|
||||
"integrity": "sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"imagemin-giflossy": {
|
||||
@@ -4969,11 +4986,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
|
||||
},
|
||||
"is-buffer": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
|
||||
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
|
||||
},
|
||||
"is-bzip2": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-bzip2/-/is-bzip2-1.0.0.tgz",
|
||||
@@ -5973,17 +5985,17 @@
|
||||
"integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.25",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz",
|
||||
"integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==",
|
||||
"version": "2.1.26",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
||||
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
||||
"requires": {
|
||||
"mime-db": "1.42.0"
|
||||
"mime-db": "1.43.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mime-db": {
|
||||
"version": "1.42.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz",
|
||||
"integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ=="
|
||||
"version": "1.43.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
||||
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -7463,6 +7475,11 @@
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
|
||||
},
|
||||
"peek-readable": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-3.1.0.tgz",
|
||||
"integrity": "sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA=="
|
||||
},
|
||||
"pend": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
@@ -8436,6 +8453,11 @@
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"readable-web-to-node-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-+oZJurc4hXpaaqsN68GoZGQAQIA3qr09Or4fqEsargABnbe5Aau8hFn6ISVleT3cpY/0n/8drn7huyyEvTbghA=="
|
||||
},
|
||||
"redent": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
|
||||
@@ -9162,9 +9184,9 @@
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
||||
},
|
||||
"string-similarity": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-3.0.0.tgz",
|
||||
"integrity": "sha512-7kS7LyTp56OqOI2BDWQNVnLX/rCxIQn+/5M0op1WV6P8Xx6TZNdajpuqQdiJ7Xx+p1C5CsWMvdiBp9ApMhxzEQ=="
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.1.tgz",
|
||||
"integrity": "sha512-v36MJzloekKVvKAsYi6O/qpn2mIuvwEFIT9Gx3yg4spkNjXYsk7yxc37g4ZTyMVIBvt/9PZGxnqEtme8XHK+Mw=="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
@@ -9271,6 +9293,16 @@
|
||||
"escape-string-regexp": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"strtok3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-5.0.1.tgz",
|
||||
"integrity": "sha512-AWliiIjyb87onqO8pM+1Hozm+PPcR4YYIWbFUT5OKQ+tOMwgdT8HwJd/IS8v3/gKdAtE5aE2p3FhcWqryuZPLQ==",
|
||||
"requires": {
|
||||
"@tokenizer/token": "^0.1.0",
|
||||
"debug": "^4.1.1",
|
||||
"peek-readable": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"sum-up": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz",
|
||||
@@ -9560,6 +9592,15 @@
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||
},
|
||||
"token-types": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/token-types/-/token-types-2.0.0.tgz",
|
||||
"integrity": "sha512-WWvu8sGK8/ZmGusekZJJ5NM6rRVTTDO7/bahz4NGiSDb/XsmdYBn6a1N/bymUHuWYTWeuLUg98wUzvE4jPdCZw==",
|
||||
"requires": {
|
||||
"@tokenizer/token": "^0.1.0",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
||||
@@ -9673,6 +9714,14 @@
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
},
|
||||
"typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||
"requires": {
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"uc.micro": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||
|
||||
14
package.json
14
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.39.5",
|
||||
"version": "0.40.7",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
@@ -21,13 +21,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async-mutex": "0.1.4",
|
||||
"axios": "0.19.0",
|
||||
"axios": "0.19.1",
|
||||
"body-parser": "1.19.0",
|
||||
"cls-hooked": "4.2.2",
|
||||
"commonmark": "0.29.0",
|
||||
"commonmark": "0.29.1",
|
||||
"cookie-parser": "1.4.4",
|
||||
"csurf": "1.10.0",
|
||||
"dayjs": "1.8.18",
|
||||
"dayjs": "1.8.19",
|
||||
"debug": "4.1.1",
|
||||
"ejs": "2.7.4",
|
||||
"electron-debug": "3.0.1",
|
||||
@@ -37,7 +37,7 @@
|
||||
"electron-window-state": "5.0.3",
|
||||
"express": "4.17.1",
|
||||
"express-session": "1.17.0",
|
||||
"file-type": "12.4.2",
|
||||
"file-type": "13.0.3",
|
||||
"fs-extra": "8.1.0",
|
||||
"helmet": "3.21.2",
|
||||
"html": "1.0.0",
|
||||
@@ -51,7 +51,7 @@
|
||||
"imagemin-pngquant": "8.0.0",
|
||||
"ini": "1.3.5",
|
||||
"jimp": "0.9.3",
|
||||
"mime-types": "2.1.25",
|
||||
"mime-types": "2.1.26",
|
||||
"moment": "2.24.0",
|
||||
"multer": "1.4.2",
|
||||
"node-abi": "2.13.0",
|
||||
@@ -69,7 +69,7 @@
|
||||
"simple-node-logger": "18.12.23",
|
||||
"sqlite": "3.0.3",
|
||||
"sqlite3": "4.1.1",
|
||||
"string-similarity": "3.0.0",
|
||||
"string-similarity": "4.0.1",
|
||||
"tar-stream": "2.1.0",
|
||||
"turndown": "5.0.3",
|
||||
"turndown-plugin-gfm": "1.0.2",
|
||||
|
||||
@@ -16,6 +16,7 @@ const sql = require('../services/sql');
|
||||
* @property {int} position
|
||||
* @property {boolean} isInheritable
|
||||
* @property {boolean} isDeleted
|
||||
* @property {string|null} deleteId - ID identifying delete transaction
|
||||
* @property {string} utcDateCreated
|
||||
* @property {string} utcDateModified
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@ const sql = require('../services/sql');
|
||||
* @property {string} prefix
|
||||
* @property {boolean} isExpanded
|
||||
* @property {boolean} isDeleted
|
||||
* @property {string|null} deleteId - ID identifying delete transaction
|
||||
* @property {string} utcDateModified
|
||||
* @property {string} utcDateCreated
|
||||
*
|
||||
@@ -25,7 +26,7 @@ class Branch extends Entity {
|
||||
static get entityName() { 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", "isDeleted", "prefix"]; }
|
||||
static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "isDeleted", "deleteId", "prefix"]; }
|
||||
|
||||
constructor(row = {}) {
|
||||
super(row);
|
||||
|
||||
@@ -11,7 +11,9 @@ const ENTITY_NAME_TO_ENTITY = {
|
||||
"attributes": Attribute,
|
||||
"branches": Branch,
|
||||
"notes": Note,
|
||||
"note_contents": Note,
|
||||
"note_revisions": NoteRevision,
|
||||
"note_revision_contents": NoteRevision,
|
||||
"recent_notes": RecentNote,
|
||||
"options": Option,
|
||||
"api_tokens": ApiToken,
|
||||
|
||||
@@ -24,6 +24,7 @@ const RELATION_DEFINITION = 'relation-definition';
|
||||
* @property {int} contentLength - length of content
|
||||
* @property {boolean} isProtected - true if note is protected
|
||||
* @property {boolean} isDeleted - true if note is deleted
|
||||
* @property {string|null} deleteId - ID identifying delete transaction
|
||||
* @property {boolean} isErased - true if note's content is erased after it has been deleted
|
||||
* @property {string} dateCreated - local date time (with offset)
|
||||
* @property {string} dateModified - local date time (with offset)
|
||||
@@ -35,7 +36,7 @@ const RELATION_DEFINITION = 'relation-definition';
|
||||
class Note extends Entity {
|
||||
static get entityName() { return "notes"; }
|
||||
static get primaryKeyName() { return "noteId"; }
|
||||
static get hashedProperties() { return ["noteId", "title", "type", "isProtected", "isDeleted"]; }
|
||||
static get hashedProperties() { return ["noteId", "title", "type", "isProtected", "isDeleted", "deleteId"]; }
|
||||
|
||||
/**
|
||||
* @param row - object containing database row from "notes" table
|
||||
@@ -109,6 +110,10 @@ class Note extends Entity {
|
||||
async getJsonContent() {
|
||||
const content = await this.getContent();
|
||||
|
||||
if (!content || !content.trim()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
@@ -118,9 +123,11 @@ class Note extends Entity {
|
||||
throw new Error(`Cannot set null content to note ${this.noteId}`);
|
||||
}
|
||||
|
||||
content = Buffer.isBuffer(content) ? content : Buffer.from(content);
|
||||
|
||||
// force updating note itself so that dateModified is represented correctly even for the content
|
||||
this.forcedChange = true;
|
||||
this.contentLength = content.length;
|
||||
this.contentLength = content.byteLength;
|
||||
await this.save();
|
||||
|
||||
this.content = content;
|
||||
@@ -129,7 +136,7 @@ class Note extends Entity {
|
||||
noteId: this.noteId,
|
||||
content: content,
|
||||
utcDateModified: dateUtils.utcNowDateTime(),
|
||||
hash: utils.hash(this.noteId + "|" + content)
|
||||
hash: utils.hash(this.noteId + "|" + content.toString())
|
||||
};
|
||||
|
||||
if (this.isProtected) {
|
||||
@@ -804,8 +811,10 @@ class Note extends Entity {
|
||||
FROM attributes
|
||||
WHERE noteId = ? AND
|
||||
isDeleted = 0 AND
|
||||
type = 'relation' AND
|
||||
name IN ('internalLink', 'imageLink', 'relationMapLink')`, [this.noteId]);
|
||||
((type = 'relation' AND
|
||||
name IN ('internalLink', 'imageLink', 'relationMapLink', 'includeNoteLink'))
|
||||
OR
|
||||
(type = 'label' AND name = 'externalLink'))`, [this.noteId]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -161,7 +161,13 @@ async function printActiveNote() {
|
||||
importCSS: false,
|
||||
loadCSS: [
|
||||
"libraries/codemirror/codemirror.css",
|
||||
"libraries/ckeditor/ckeditor-content.css"
|
||||
"libraries/ckeditor/ckeditor-content.css",
|
||||
"libraries/ckeditor/ckeditor-content.css",
|
||||
"libraries/bootstrap/css/bootstrap.min.css",
|
||||
"stylesheets/print.css",
|
||||
"stylesheets/relation_map.css",
|
||||
"stylesheets/themes.css",
|
||||
"stylesheets/detail.css"
|
||||
],
|
||||
debug: true
|
||||
});
|
||||
|
||||
@@ -10,8 +10,6 @@ const $buildRevision = $("#build-revision");
|
||||
const $dataDirectory = $("#data-directory");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
const appInfo = await server.get('app-info');
|
||||
|
||||
$appVersion.text(appInfo.appVersion);
|
||||
@@ -22,7 +20,5 @@ export async function showDialog() {
|
||||
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
|
||||
$dataDirectory.text(appInfo.dataDirectory);
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
@@ -11,13 +11,9 @@ const $linkTitle = $("#link-title");
|
||||
const $addLinkTitleFormGroup = $("#add-link-title-form-group");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
$addLinkTitleFormGroup.toggle(!hasSelection());
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$autoComplete.val('').trigger('focus');
|
||||
$linkTitle.val('');
|
||||
|
||||
@@ -287,8 +287,6 @@ function initKoPlugins() {
|
||||
}
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
await libraryLoader.requireLibrary(libraryLoader.KNOCKOUT);
|
||||
|
||||
// lazily apply bindings on first use
|
||||
@@ -300,11 +298,9 @@ export async function showDialog() {
|
||||
ko.applyBindings(attributesModel, $dialog[0]);
|
||||
}
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
await attributesModel.loadAttributes();
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
|
||||
$dialog.on('focus', '.attribute-name', function (e) {
|
||||
|
||||
@@ -6,11 +6,7 @@ const $backendLogTextArea = $("#backend-log-textarea");
|
||||
const $refreshBackendLog = $("#refresh-backend-log-button");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@@ -13,10 +13,6 @@ const $noteTitle = $('#branch-prefix-note-title');
|
||||
let branchId;
|
||||
|
||||
export async function showDialog(node) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
branchId = node.data.branchId;
|
||||
const branch = treeCache.getBranch(branchId);
|
||||
|
||||
@@ -30,7 +26,7 @@ export async function showDialog(node) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$treePrefixInput.val(branch.prefix);
|
||||
|
||||
|
||||
@@ -22,11 +22,7 @@ export async function showDialog(noteIds) {
|
||||
}
|
||||
}
|
||||
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$noteAutoComplete.val('').trigger('focus');
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ let taskId = '';
|
||||
let branchId = null;
|
||||
|
||||
export async function showDialog(node, defaultType) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
// each opening of the dialog resets the taskId so we don't associate it with previous exports anymore
|
||||
taskId = '';
|
||||
$exportButton.removeAttr("disabled");
|
||||
@@ -38,9 +36,7 @@ export async function showDialog(node, defaultType) {
|
||||
|
||||
$("#opml-v2").prop("checked", true); // setting default
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
branchId = node.data.branchId;
|
||||
|
||||
|
||||
@@ -3,9 +3,5 @@ import utils from "../services/utils.js";
|
||||
const $dialog = $("#help-dialog");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
@@ -16,8 +16,6 @@ const $explodeArchivesCheckbox = $("#explode-archives-checkbox");
|
||||
let parentNoteId = null;
|
||||
|
||||
export async function showDialog(node) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
$fileUploadInput.val('').trigger('change'); // to trigger Import button disabling listener below
|
||||
|
||||
$safeImportCheckbox.prop("checked", true);
|
||||
@@ -26,13 +24,11 @@ export async function showDialog(node) {
|
||||
$codeImportedAsCodeCheckbox.prop("checked", true);
|
||||
$explodeArchivesCheckbox.prop("checked", true);
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
parentNoteId = node.data.noteId;
|
||||
|
||||
$noteTitle.text(await treeUtils.getNoteTitle(parentNoteId));
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
|
||||
$form.on('submit', () => {
|
||||
|
||||
@@ -10,13 +10,9 @@ let callback = null;
|
||||
export async function showDialog(cb) {
|
||||
callback = cb;
|
||||
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$autoComplete.val('');
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
noteAutocompleteService.initNoteAutocomplete($autoComplete, { hideGoToSelectedNoteButton: true });
|
||||
noteAutocompleteService.showRecentNotes($autoComplete);
|
||||
|
||||
@@ -10,13 +10,9 @@ let $originallyFocused; // element focused before the dialog was opened so we ca
|
||||
export function info(message) {
|
||||
$originallyFocused = $(':focus');
|
||||
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$infoContent.text(message);
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
return new Promise((res, rej) => { resolve = res; });
|
||||
}
|
||||
|
||||
@@ -8,13 +8,9 @@ const $autoComplete = $("#jump-to-note-autocomplete");
|
||||
const $showInFullTextButton = $("#show-in-full-text-button");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$autoComplete.val('');
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
noteAutocompleteService.initNoteAutocomplete($autoComplete, { hideGoToSelectedNoteButton: true })
|
||||
.on('autocomplete:selected', function(event, suggestion, dataset) {
|
||||
|
||||
@@ -16,10 +16,6 @@ function getOptions() {
|
||||
}
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
// set default settings
|
||||
$maxNotesInput.val(20);
|
||||
|
||||
@@ -27,7 +23,7 @@ export async function showDialog() {
|
||||
|
||||
$linkMapContainer.empty();
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
|
||||
$dialog.on('shown.bs.modal', () => {
|
||||
|
||||
@@ -37,9 +37,7 @@ export async function importMarkdownInline() {
|
||||
convertMarkdownToHtml(text);
|
||||
}
|
||||
else {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import noteAutocompleteService from "../services/note_autocomplete.js";
|
||||
import utils from "../services/utils.js";
|
||||
import cloningService from "../services/cloning.js";
|
||||
import treeUtils from "../services/tree_utils.js";
|
||||
import toastService from "../services/toast.js";
|
||||
import treeCache from "../services/tree_cache.js";
|
||||
import treeChangesService from "../services/branches.js";
|
||||
@@ -18,11 +16,7 @@ let movedNodes;
|
||||
export async function showDialog(nodes) {
|
||||
movedNodes = nodes;
|
||||
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$noteAutoComplete.val('').trigger('focus');
|
||||
|
||||
|
||||
@@ -10,11 +10,7 @@ const $mime = $("#note-info-mime");
|
||||
const $okButton = $("#note-info-ok-button");
|
||||
|
||||
export function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
const activeNote = noteDetailService.getActiveTabNote();
|
||||
|
||||
|
||||
@@ -29,11 +29,7 @@ export async function showCurrentNoteRevisions() {
|
||||
}
|
||||
|
||||
export async function showNoteRevisionsDialog(noteId, noteRevisionId) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
await loadNoteRevisions(noteId, noteRevisionId);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,7 @@ const $dialog = $("#note-source-dialog");
|
||||
const $noteSource = $("#note-source");
|
||||
|
||||
export function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
const noteText = noteDetailService.getActiveTabNote().content;
|
||||
|
||||
|
||||
@@ -6,13 +6,9 @@ import utils from "../services/utils.js";
|
||||
const $dialog = $("#options-dialog");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
const options = await server.get('options');
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
(await Promise.all([
|
||||
import('./options/advanced.js'),
|
||||
|
||||
@@ -6,7 +6,7 @@ const TPL = `
|
||||
<div>
|
||||
<h4>Spell check</h4>
|
||||
|
||||
<p>These options apply only for desktop builds, browsers will use their own native spell check.</p>
|
||||
<p>These options apply only for desktop builds, browsers will use their own native spell check. App restart is required after change.</p>
|
||||
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="spell-check-enabled">
|
||||
@@ -37,6 +37,20 @@ const TPL = `
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>Note erasure timeout</h4>
|
||||
|
||||
<p>Deleted notes are at first only marked as deleted and it is possible to recover them
|
||||
from Recent Notes dialog. After period of time, deleted notes are "erased" which means
|
||||
their content is not recoverable anymore. This setting allows you to configure the length
|
||||
of the period between deleting and erasing the note.</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="erase-notes-after-time-in-seconds">Erase notes after X seconds</label>
|
||||
<input class="form-control" id="erase-notes-after-time-in-seconds" type="number" min="0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>Protected session timeout</h4>
|
||||
|
||||
@@ -81,6 +95,20 @@ export default class ProtectedSessionOptions {
|
||||
return false;
|
||||
});
|
||||
|
||||
this.$eraseNotesAfterTimeInSeconds = $("#erase-notes-after-time-in-seconds");
|
||||
|
||||
this.$eraseNotesAfterTimeInSeconds.on('change', () => {
|
||||
const eraseNotesAfterTimeInSeconds = this.$eraseNotesAfterTimeInSeconds.val();
|
||||
|
||||
server.put('options', { 'eraseNotesAfterTimeInSeconds': eraseNotesAfterTimeInSeconds }).then(() => {
|
||||
optionsService.reloadOptions();
|
||||
|
||||
toastService.showMessage("Options change have been saved.");
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
this.$protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
|
||||
|
||||
this.$protectedSessionTimeout.on('change', () => {
|
||||
@@ -126,6 +154,7 @@ export default class ProtectedSessionOptions {
|
||||
this.$spellCheckEnabled.prop("checked", options['spellCheckEnabled'] === 'true');
|
||||
this.$spellCheckLanguageCode.val(options['spellCheckLanguageCode']);
|
||||
|
||||
this.$eraseNotesAfterTimeInSeconds.val(options['eraseNotesAfterTimeInSeconds']);
|
||||
this.$protectedSessionTimeout.val(options['protectedSessionTimeout']);
|
||||
this.$noteRevisionsTimeInterval.val(options['noteRevisionSnapshotTimeInterval']);
|
||||
|
||||
|
||||
@@ -12,10 +12,6 @@ let resolve;
|
||||
let shownCb;
|
||||
|
||||
export function ask({ message, defaultValue, shown }) {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
shownCb = shown;
|
||||
|
||||
$question = $("<label>")
|
||||
@@ -34,7 +30,7 @@ export function ask({ message, defaultValue, shown }) {
|
||||
.append($question)
|
||||
.append($answer));
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
return new Promise((res, rej) => { resolve = res; });
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import protectedSessionService from "../services/protected_session.js";
|
||||
import utils from "../services/utils.js";
|
||||
|
||||
const $dialog = $("#protected-session-password-dialog");
|
||||
const $passwordForm = $dialog.find(".protected-session-password-form");
|
||||
const $passwordInput = $dialog.find(".protected-session-password");
|
||||
|
||||
export function show() {
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
$passwordInput.trigger('focus');
|
||||
}
|
||||
|
||||
@@ -8,11 +8,7 @@ const $dialog = $("#recent-changes-dialog");
|
||||
const $content = $("#recent-changes-content");
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
|
||||
const result = await server.get('recent-changes');
|
||||
|
||||
@@ -28,31 +24,60 @@ export async function showDialog() {
|
||||
const groupedByDate = groupByDate(result);
|
||||
|
||||
for (const [dateDay, dayChanges] of groupedByDate) {
|
||||
const changesListEl = $('<ul>');
|
||||
const $changesList = $('<ul>');
|
||||
|
||||
const dayEl = $('<div>').append($('<b>').html(utils.formatDate(dateDay))).append(changesListEl);
|
||||
const dayEl = $('<div>').append($('<b>').html(utils.formatDate(dateDay))).append($changesList);
|
||||
|
||||
for (const change of dayChanges) {
|
||||
const formattedTime = utils.formatTime(utils.parseDate(change.date));
|
||||
|
||||
let noteLink;
|
||||
let $noteLink;
|
||||
|
||||
if (change.current_isDeleted) {
|
||||
noteLink = change.current_title;
|
||||
$noteLink = $("<span>").text(change.current_title);
|
||||
|
||||
if (change.canBeUndeleted) {
|
||||
const $undeleteLink = $(`<a href="javascript:">`)
|
||||
.text("undelete")
|
||||
.on('click', async () => {
|
||||
const confirmDialog = await import('../dialogs/confirm.js');
|
||||
const text = 'Do you want to undelete this note and its sub-notes?';
|
||||
|
||||
if (await confirmDialog.confirm(text)) {
|
||||
await server.put(`notes/${change.noteId}/undelete`);
|
||||
|
||||
$dialog.modal('hide');
|
||||
|
||||
await treeCache.reloadNotes([change.noteId]);
|
||||
|
||||
treeService.activateNote(change.noteId);
|
||||
}
|
||||
});
|
||||
|
||||
$noteLink
|
||||
.append(' (')
|
||||
.append($undeleteLink)
|
||||
.append(')');
|
||||
}
|
||||
}
|
||||
else {
|
||||
const note = await treeCache.getNote(change.noteId);
|
||||
const notePath = await treeService.getSomeNotePath(note);
|
||||
|
||||
noteLink = await linkService.createNoteLink(notePath, {
|
||||
title: change.title,
|
||||
showNotePath: true
|
||||
});
|
||||
if (notePath) {
|
||||
$noteLink = await linkService.createNoteLink(notePath, {
|
||||
title: change.title,
|
||||
showNotePath: true
|
||||
});
|
||||
}
|
||||
else {
|
||||
$noteLink = $("<span>").text(note.title);
|
||||
}
|
||||
}
|
||||
|
||||
changesListEl.append($('<li>')
|
||||
$changesList.append($('<li>')
|
||||
.append(formattedTime + ' - ')
|
||||
.append(noteLink));
|
||||
.append($noteLink));
|
||||
}
|
||||
|
||||
$content.append(dayEl);
|
||||
@@ -85,5 +110,6 @@ function groupByDate(result) {
|
||||
|
||||
groupedByDate.get(dateDay).push(row);
|
||||
}
|
||||
|
||||
return groupedByDate;
|
||||
}
|
||||
|
||||
@@ -14,13 +14,9 @@ let codeEditor;
|
||||
$dialog.on("shown.bs.modal", e => initEditor());
|
||||
|
||||
export async function showDialog() {
|
||||
utils.closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
await showTableSchemas();
|
||||
|
||||
$dialog.modal();
|
||||
utils.openDialog($dialog);
|
||||
}
|
||||
|
||||
async function initEditor() {
|
||||
|
||||
@@ -7,10 +7,34 @@ import contextMenuWidget from "./services/context_menu.js";
|
||||
import treeChangesService from "./services/branches.js";
|
||||
import utils from "./services/utils.js";
|
||||
import treeUtils from "./services/tree_utils.js";
|
||||
import linkService from "./services/link.js";
|
||||
import noteContentRenderer from "./services/note_content_renderer.js";
|
||||
import server from "./services/server.js";
|
||||
|
||||
window.glob.isDesktop = utils.isDesktop;
|
||||
window.glob.isMobile = utils.isMobile;
|
||||
|
||||
// required for CKEditor image upload plugin
|
||||
window.glob.getActiveNode = treeService.getActiveNode;
|
||||
window.glob.getHeaders = server.getHeaders;
|
||||
window.glob.noteChanged = noteDetailService.noteChanged;
|
||||
window.glob.refreshTree = treeService.reload;
|
||||
window.glob.showAddLinkDialog = () => import('./dialogs/add_link.js').then(d => d.showDialog());
|
||||
window.glob.showIncludeNoteDialog = cb => import('./dialogs/include_note.js').then(d => d.showDialog(cb));
|
||||
window.glob.loadIncludedNote = async (noteId, el) => {
|
||||
const note = await treeCache.getNote(noteId);
|
||||
|
||||
if (note) {
|
||||
$(el).empty().append($("<h3>").append(await linkService.createNoteLink(note.noteId, {
|
||||
showTooltip: false
|
||||
})));
|
||||
|
||||
const {renderedContent} = await noteContentRenderer.getRenderedContent(note);
|
||||
|
||||
$(el).append(renderedContent);
|
||||
}
|
||||
};
|
||||
|
||||
const $leftPane = $("#left-pane");
|
||||
const $tree = $("#tree");
|
||||
const $detail = $("#detail");
|
||||
@@ -37,12 +61,12 @@ $detail.on("click", ".close-detail-button",() => {
|
||||
});
|
||||
|
||||
async function showTree() {
|
||||
const tree = await treeService.loadTree();
|
||||
const treeData = await treeService.loadTreeData();
|
||||
|
||||
$tree.fancytree({
|
||||
autoScroll: true,
|
||||
extensions: ["dnd5", "clones"],
|
||||
source: tree,
|
||||
source: treeData,
|
||||
scrollParent: $tree,
|
||||
minExpandLevel: 2, // root can't be collapsed
|
||||
click: (event, data) => {
|
||||
@@ -87,6 +111,8 @@ async function showTree() {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
treeService.setTree($.ui.fancytree.getTree("#tree"));
|
||||
}
|
||||
|
||||
$detail.on("click", ".note-menu-button", async e => {
|
||||
|
||||
@@ -265,6 +265,24 @@ ws.subscribeToMessages(async message => {
|
||||
}
|
||||
});
|
||||
|
||||
ws.subscribeToMessages(async message => {
|
||||
if (message.taskType !== 'undelete-notes') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.type === 'task-error') {
|
||||
toastService.closePersistent(message.taskId);
|
||||
toastService.showError(message.message);
|
||||
} else if (message.type === 'task-progress-count') {
|
||||
toastService.showPersistent(makeToast(message.taskId, "Undeleting notes in progress: " + message.progressCount));
|
||||
} else if (message.type === 'task-succeeded') {
|
||||
const toast = makeToast(message.taskId, "Undeleting notes finished successfully.");
|
||||
toast.closeAfter = 5000;
|
||||
|
||||
toastService.showPersistent(toast);
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
moveBeforeNode,
|
||||
moveAfterNode,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import treeUtils from "./tree_utils.js";
|
||||
import treeService from "./tree.js";
|
||||
import treeChangesService from "./branches.js";
|
||||
import cloningService from "./cloning.js";
|
||||
import toastService from "./toast.js";
|
||||
@@ -19,7 +19,7 @@ async function pasteAfter(afterNode) {
|
||||
}
|
||||
|
||||
if (clipboardMode === 'cut') {
|
||||
const nodes = clipboardNodeKeys.map(nodeKey => treeUtils.getNodeByKey(nodeKey));
|
||||
const nodes = clipboardNodeKeys.map(nodeKey => treeService.getNodeByKey(nodeKey));
|
||||
|
||||
await treeChangesService.moveAfterNode(nodes, afterNode);
|
||||
|
||||
@@ -28,7 +28,7 @@ async function pasteAfter(afterNode) {
|
||||
}
|
||||
else if (clipboardMode === 'copy') {
|
||||
for (const nodeKey of clipboardNodeKeys) {
|
||||
const clipNode = treeUtils.getNodeByKey(nodeKey);
|
||||
const clipNode = treeService.getNodeByKey(nodeKey);
|
||||
|
||||
await cloningService.cloneNoteAfter(clipNode.data.noteId, afterNode.data.branchId);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ async function pasteInto(parentNode) {
|
||||
}
|
||||
|
||||
if (clipboardMode === 'cut') {
|
||||
const nodes = clipboardNodeKeys.map(nodeKey => treeUtils.getNodeByKey(nodeKey));
|
||||
const nodes = clipboardNodeKeys.map(nodeKey => treeService.getNodeByKey(nodeKey));
|
||||
|
||||
await treeChangesService.moveToNode(nodes, parentNode);
|
||||
|
||||
@@ -57,7 +57,7 @@ async function pasteInto(parentNode) {
|
||||
}
|
||||
else if (clipboardMode === 'copy') {
|
||||
for (const nodeKey of clipboardNodeKeys) {
|
||||
const clipNode = treeUtils.getNodeByKey(nodeKey);
|
||||
const clipNode = treeService.getNodeByKey(nodeKey);
|
||||
|
||||
await cloningService.cloneNoteTo(clipNode.data.noteId, parentNode.data.noteId);
|
||||
}
|
||||
@@ -92,7 +92,7 @@ function cut(nodes) {
|
||||
}
|
||||
|
||||
function isClipboardEmpty() {
|
||||
clipboardNodeKeys = clipboardNodeKeys.filter(key => !!treeUtils.getNodeByKey(key));
|
||||
clipboardNodeKeys = clipboardNodeKeys.filter(key => !!treeService.getNodeByKey(key));
|
||||
|
||||
return clipboardNodeKeys.length === 0;
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ function registerEntrypoints() {
|
||||
|
||||
d.showDialog(selectedOrActiveNodes);
|
||||
}));
|
||||
|
||||
|
||||
keyboardActionService.setGlobalActionHandler("CreateNoteIntoDayNote", async () => {
|
||||
const todayNote = await dateNoteService.getTodayNote();
|
||||
|
||||
@@ -288,6 +288,8 @@ function registerEntrypoints() {
|
||||
|
||||
searchNotesService.searchInSubtree(node.data.noteId);
|
||||
});
|
||||
|
||||
keyboardActionService.setGlobalActionHandler("CopyWithoutFormatting", utils.copySelectionToClipboard);
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -296,12 +296,28 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
|
||||
*/
|
||||
this.createNoteLink = linkService.createNoteLink;
|
||||
|
||||
/**
|
||||
* Adds given text to the editor cursor
|
||||
*
|
||||
* @param {string} text - this must be clear text, HTML is not supported.
|
||||
* @method
|
||||
*/
|
||||
this.addTextToActiveTabEditor = linkService.addTextToEditor;
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @returns {NoteFull} active note (loaded into right pane)
|
||||
*/
|
||||
this.getActiveTabNote = noteDetailService.getActiveTabNote;
|
||||
|
||||
/**
|
||||
* See https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editor-Editor.html for a documentation on the returned instance.
|
||||
*
|
||||
* @method
|
||||
* @returns {Editor|null} CKEditor instance or null (e.g. if active note is not a text note)
|
||||
*/
|
||||
this.getActiveTabTextEditor = noteDetailService.getActiveEditor;
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
||||
|
||||
@@ -10,6 +10,12 @@ function getNotePathFromUrl(url) {
|
||||
}
|
||||
|
||||
async function createNoteLink(notePath, options = {}) {
|
||||
if (!notePath || !notePath.trim()) {
|
||||
console.error("Missing note path");
|
||||
|
||||
return $("<span>").text("[missing note]");
|
||||
}
|
||||
|
||||
let noteTitle = options.title;
|
||||
const showTooltip = options.showTooltip === undefined ? true : options.showTooltip;
|
||||
const showNotePath = options.showNotePath === undefined ? false : options.showNotePath;
|
||||
|
||||
@@ -7,31 +7,22 @@ import protectedSessionHolder from "./protected_session_holder.js";
|
||||
async function getRenderedContent(note) {
|
||||
const type = getRenderingType(note);
|
||||
|
||||
let rendered;
|
||||
let $rendered;
|
||||
|
||||
if (type === 'text') {
|
||||
const fullNote = await server.get('notes/' + note.noteId);
|
||||
|
||||
const $content = $("<div>").html(fullNote.content);
|
||||
|
||||
if (utils.isHtmlEmpty(fullNote.content)) {
|
||||
rendered = "";
|
||||
}
|
||||
else {
|
||||
rendered = $content;
|
||||
}
|
||||
$rendered = $("<div>").html(fullNote.content);
|
||||
}
|
||||
else if (type === 'code') {
|
||||
const fullNote = await server.get('notes/' + note.noteId);
|
||||
|
||||
if (fullNote.content.trim() === "") {
|
||||
rendered = "";
|
||||
}
|
||||
|
||||
rendered = $("<pre>").text(fullNote.content);
|
||||
$rendered = $("<pre>").text(fullNote.content);
|
||||
}
|
||||
else if (type === 'image') {
|
||||
rendered = $("<img>").attr("src", `api/images/${note.noteId}/${note.title}`);
|
||||
$rendered = $("<img>")
|
||||
.attr("src", `api/images/${note.noteId}/${note.title}`)
|
||||
.css("max-width", "100%");
|
||||
}
|
||||
else if (type === 'file') {
|
||||
function getFileUrl() {
|
||||
@@ -56,33 +47,35 @@ async function getRenderedContent(note) {
|
||||
// open doesn't work for protected notes since it works through browser which isn't in protected session
|
||||
$openButton.toggle(!note.isProtected);
|
||||
|
||||
rendered = $('<div>')
|
||||
$rendered = $('<div>')
|
||||
.append($downloadButton)
|
||||
.append(' ')
|
||||
.append($openButton);
|
||||
}
|
||||
else if (type === 'render') {
|
||||
const $el = $('<div>');
|
||||
$rendered = $('<div>');
|
||||
|
||||
await renderService.render(note, $el, this.ctx);
|
||||
|
||||
rendered = $el;
|
||||
await renderService.render(note, $rendered, this.ctx);
|
||||
}
|
||||
else if (type === 'protected-session') {
|
||||
const $button = $(`<button class="btn btn-sm"><span class="bx bx-log-in"></span> Enter protected session</button>`)
|
||||
.on('click', protectedSessionService.enterProtectedSession);
|
||||
|
||||
rendered = $("<div>")
|
||||
$rendered = $("<div>")
|
||||
.append("<div>This note is protected and to access it you need to enter password.</div>")
|
||||
.append("<br/>")
|
||||
.append($button);
|
||||
}
|
||||
else {
|
||||
rendered = "<em>Content of this note cannot be displayed in the book format</em>";
|
||||
$rendered = $("<em>Content of this note cannot be displayed in the book format</em>");
|
||||
}
|
||||
|
||||
if (note.cssClass) {
|
||||
$rendered.addClass(note.cssClass);
|
||||
}
|
||||
|
||||
return {
|
||||
renderedContent: rendered,
|
||||
renderedContent: $rendered,
|
||||
type
|
||||
};
|
||||
}
|
||||
|
||||
@@ -148,8 +148,8 @@ class NoteDetailBook {
|
||||
const label = `${childCount} child${childCount > 1 ? 'ren' : ''}`;
|
||||
|
||||
$card.append($('<div class="note-book-children">')
|
||||
.append($(`<a class="note-book-open-children-button" href="javascript:">+ Show ${label}</a>`))
|
||||
.append($(`<a class="note-book-hide-children-button" href="javascript:">- Hide ${label}</a>`).hide())
|
||||
.append($(`<a class="note-book-open-children-button no-print" href="javascript:">+ Show ${label}</a>`))
|
||||
.append($(`<a class="note-book-hide-children-button no-print" href="javascript:">- Hide ${label}</a>`).hide())
|
||||
.append($('<div class="note-book-children-content">'))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ class NoteDetailCode {
|
||||
// CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check)
|
||||
// we provide fallback
|
||||
this.codeEditor.setValue(this.ctx.note.content || "");
|
||||
this.codeEditor.clearHistory();
|
||||
|
||||
const info = CodeMirror.findModeByMIME(this.ctx.note.mime);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import treeService from './tree.js';
|
||||
import treeCache from "./tree_cache.js";
|
||||
import server from './server.js';
|
||||
import toastService from "./toast.js";
|
||||
import utils from "./utils.js";
|
||||
|
||||
const $searchInput = $("input[name='search-text']");
|
||||
const $resetSearchButton = $("#reset-search-button");
|
||||
@@ -28,6 +29,8 @@ const helpText = `
|
||||
</p>`;
|
||||
|
||||
function showSearch() {
|
||||
utils.saveFocusedElement();
|
||||
|
||||
$searchBox.slideDown();
|
||||
|
||||
$searchBox.tooltip({
|
||||
@@ -49,6 +52,8 @@ function hideSearch() {
|
||||
|
||||
$searchResults.hide();
|
||||
$searchBox.slideUp();
|
||||
|
||||
utils.focusSavedElement();
|
||||
}
|
||||
|
||||
function toggleSearch() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import splitService from "./split.js";
|
||||
import optionService from "./options.js";
|
||||
import server from "./server.js";
|
||||
import noteDetailService from "./note_detail.js";
|
||||
import utils from "./utils.js";
|
||||
|
||||
const $sidebar = $("#right-pane");
|
||||
const $sidebarContainer = $('#sidebar-container');
|
||||
@@ -15,6 +16,10 @@ const $hideSidebarButton = $("#hide-sidebar-button");
|
||||
optionService.waitForOptions().then(options => toggleSidebar(options.is('rightPaneVisible')));
|
||||
|
||||
function toggleSidebar(show) {
|
||||
if (utils.isMobile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sidebar.toggle(show);
|
||||
$showSidebarButton.toggle(!show);
|
||||
$hideSidebarButton.toggle(show);
|
||||
|
||||
@@ -3,15 +3,16 @@ import optionsService from "./options.js";
|
||||
export async function initSpellCheck() {
|
||||
const options = await optionsService.waitForOptions();
|
||||
|
||||
if (!options.is('spellCheckEnabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker');
|
||||
const {remote, shell} = require('electron');
|
||||
|
||||
const spellCheckHandler = new SpellCheckHandler();
|
||||
spellCheckHandler.attachToInput();
|
||||
|
||||
// not fully disabling the spellcheck since we want to preserve the context menu
|
||||
// this will just get rid of the "red squiggles"
|
||||
if (options.is('spellCheckEnabled')) {
|
||||
spellCheckHandler.attachToInput();
|
||||
}
|
||||
|
||||
spellCheckHandler.switchLanguage(options.get('spellCheckLanguageCode'));
|
||||
|
||||
|
||||
@@ -303,7 +303,11 @@ class TabContext {
|
||||
|
||||
let type = this.note.type;
|
||||
|
||||
if (type === 'text' && !disableAutoBook && utils.isHtmlEmpty(this.note.content) && this.note.hasChildren()) {
|
||||
if (type === 'text'
|
||||
&& !disableAutoBook
|
||||
&& utils.isHtmlEmpty(this.note.content)
|
||||
&& this.note.hasChildren()
|
||||
&& utils.isDesktop()) {
|
||||
type = 'book';
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import TreeContextMenu from "./tree_context_menu.js";
|
||||
import bundle from "./bundle.js";
|
||||
import keyboardActionService from "./keyboard_actions.js";
|
||||
|
||||
let tree;
|
||||
|
||||
const $tree = $("#tree");
|
||||
const $createTopLevelNoteButton = $("#create-top-level-note-button");
|
||||
const $collapseTreeButton = $("#collapse-tree-button");
|
||||
@@ -30,8 +32,6 @@ const frontendLoaded = new Promise(resolve => { setFrontendAsLoaded = resolve; }
|
||||
* @return {FancytreeNode|null}
|
||||
*/
|
||||
function getFocusedNode() {
|
||||
const tree = $tree.fancytree("getTree");
|
||||
|
||||
return tree.getFocusNode();
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ function getFocusedNode() {
|
||||
* @return {FancytreeNode|null}
|
||||
*/
|
||||
function getActiveNode() {
|
||||
return $tree.fancytree("getActiveNode");
|
||||
return tree.getActiveNode();
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
@@ -56,7 +56,7 @@ async function getNodesByBranchId(branchId) {
|
||||
function getNodesByNoteId(noteId) {
|
||||
utils.assertArguments(noteId);
|
||||
|
||||
const list = getTree().getNodesByRef(noteId);
|
||||
const list = tree.getNodesByRef(noteId);
|
||||
return list ? list : []; // if no nodes with this refKey are found, fancy tree returns null
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ async function getSomeNotePath(note) {
|
||||
const parents = await cur.getParentNotes();
|
||||
|
||||
if (!parents.length) {
|
||||
toastService.throwError(`Can't find parents for note ${cur.noteId}`);
|
||||
console.error(`Can't find parents for note ${cur.noteId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ async function setExpandedToServer(branchId, isExpanded) {
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
function getSelectedNodes(stopOnParents = false) {
|
||||
return getTree().getSelectedNodes(stopOnParents);
|
||||
return tree.getSelectedNodes(stopOnParents);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
@@ -429,14 +429,14 @@ async function treeInitialized() {
|
||||
setFrontendAsLoaded();
|
||||
}
|
||||
|
||||
async function initFancyTree(tree) {
|
||||
utils.assertArguments(tree);
|
||||
async function initFancyTree(treeData) {
|
||||
utils.assertArguments(treeData);
|
||||
|
||||
$tree.fancytree({
|
||||
autoScroll: true,
|
||||
keyboard: false, // we takover keyboard handling in the hotkeys plugin
|
||||
extensions: ["hotkeys", "dnd5", "clones"],
|
||||
source: tree,
|
||||
source: treeData,
|
||||
scrollParent: $tree,
|
||||
minExpandLevel: 2, // root can't be collapsed
|
||||
click: (event, data) => {
|
||||
@@ -523,19 +523,16 @@ async function initFancyTree(tree) {
|
||||
|
||||
return false; // blocks default browser right click menu
|
||||
});
|
||||
}
|
||||
|
||||
/** @return {Fancytree} */
|
||||
function getTree() {
|
||||
return $tree.fancytree('getTree');
|
||||
tree = $.ui.fancytree.getTree("#tree");
|
||||
}
|
||||
|
||||
async function reload() {
|
||||
const notes = await loadTree();
|
||||
const notes = await loadTreeData();
|
||||
|
||||
const activeNotePath = getActiveNode() !== null ? await treeUtils.getNotePath(getActiveNode()) : null;
|
||||
|
||||
await getTree().reload(notes);
|
||||
await tree.reload(notes);
|
||||
|
||||
// reactivate originally activated node, but don't trigger note loading
|
||||
if (activeNotePath) {
|
||||
@@ -559,7 +556,7 @@ function getHashValueFromAddress() {
|
||||
return str.split("-");
|
||||
}
|
||||
|
||||
async function loadTree() {
|
||||
async function loadTreeData() {
|
||||
const resp = await server.get('tree');
|
||||
|
||||
treeCache.load(resp.notes, resp.branches);
|
||||
@@ -580,7 +577,7 @@ async function collapseTree(node = null) {
|
||||
}
|
||||
|
||||
function focusTree() {
|
||||
getTree().setFocus();
|
||||
tree.setFocus();
|
||||
}
|
||||
|
||||
async function scrollToActiveNote() {
|
||||
@@ -638,7 +635,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
|
||||
extraOptions.saveSelection = false;
|
||||
}
|
||||
|
||||
if (extraOptions.saveSelection) {
|
||||
if (extraOptions.saveSelection && utils.isCKEditorInitialized()) {
|
||||
[extraOptions.title, extraOptions.content] = parseSelectedHtml(window.cutToNote.getSelectedHtml());
|
||||
}
|
||||
|
||||
@@ -651,7 +648,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
|
||||
type: extraOptions.type
|
||||
});
|
||||
|
||||
if (extraOptions.saveSelection) {
|
||||
if (extraOptions.saveSelection && utils.isCKEditorInitialized()) {
|
||||
// we remove the selection only after it was saved to server to make sure we don't lose anything
|
||||
window.cutToNote.removeSelection();
|
||||
}
|
||||
@@ -754,9 +751,9 @@ async function sortAlphabetically(noteId) {
|
||||
}
|
||||
|
||||
async function showTree() {
|
||||
const tree = await loadTree();
|
||||
const treeData = await loadTreeData();
|
||||
|
||||
await initFancyTree(tree);
|
||||
await initFancyTree(treeData);
|
||||
}
|
||||
|
||||
ws.subscribeToMessages(message => {
|
||||
@@ -873,7 +870,7 @@ window.glob.cutIntoNote = () => createNoteInto(true);
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('CutIntoNote', () => createNoteInto(true));
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('CreateNoteInto', createNoteInto);
|
||||
keyboardActionService.setGlobalActionHandler('CreateNoteInto', () => createNoteInto(true));
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('ScrollToActiveNote', scrollToActiveNote);
|
||||
|
||||
@@ -912,6 +909,13 @@ async function duplicateNote(noteId, parentNoteId) {
|
||||
toastService.showMessage(`Note "${origNote.title}" has been duplicated`);
|
||||
}
|
||||
|
||||
function getNodeByKey(key) {
|
||||
return tree.getNodeByKey(key);
|
||||
}
|
||||
|
||||
function setTree(treeInstance) {
|
||||
tree = treeInstance;
|
||||
}
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('CollapseTree', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument
|
||||
$collapseTreeButton.on('click', () => collapseTree());
|
||||
@@ -931,13 +935,12 @@ export default {
|
||||
setNoteTitle,
|
||||
setPrefix,
|
||||
createNote,
|
||||
createNoteInto,
|
||||
getSelectedNodes,
|
||||
getSelectedOrActiveNodes,
|
||||
clearSelectedNodes,
|
||||
sortAlphabetically,
|
||||
showTree,
|
||||
loadTree,
|
||||
loadTreeData,
|
||||
treeInitialized,
|
||||
setExpandedToServer,
|
||||
getNodesByNoteId,
|
||||
@@ -949,5 +952,7 @@ export default {
|
||||
getSomeNotePath,
|
||||
focusTree,
|
||||
scrollToActiveNote,
|
||||
duplicateNote
|
||||
duplicateNote,
|
||||
getNodeByKey,
|
||||
setTree
|
||||
};
|
||||
@@ -62,6 +62,11 @@ class TreeContextMenu {
|
||||
!isHoisted || !isNotRoot ? null : { title: 'Unhoist note <kbd data-kb-action="ToggleNoteHoisting"></kbd>', cmd: "unhoist", uiIcon: "arrow-up" },
|
||||
{ title: 'Edit branch prefix <kbd data-kb-action="EditBranchPrefix"></kbd>', cmd: "editBranchPrefix", uiIcon: "empty",
|
||||
enabled: isNotRoot && parentNotSearch && noSelectedNotes},
|
||||
{ title: "Advanced", uiIcon: "empty", enabled: true, items: [
|
||||
{ title: 'Collapse subtree <kbd data-kb-action="CollapseSubtree"></kbd>', cmd: "collapseSubtree", uiIcon: "align-justify", enabled: noSelectedNotes },
|
||||
{ title: "Force note sync", cmd: "forceNoteSync", uiIcon: "recycle", enabled: noSelectedNotes },
|
||||
{ title: 'Sort alphabetically <kbd data-kb-action="SortChildNotes"></kbd>', cmd: "sortAlphabetically", uiIcon: "empty", enabled: noSelectedNotes && notSearch }
|
||||
] },
|
||||
{ title: "----" },
|
||||
{ title: "Protect subtree", cmd: "protectSubtree", uiIcon: "check-shield", enabled: noSelectedNotes },
|
||||
{ title: "Unprotect subtree", cmd: "unprotectSubtree", uiIcon: "shield", enabled: noSelectedNotes },
|
||||
@@ -84,12 +89,7 @@ class TreeContextMenu {
|
||||
{ title: "Export", cmd: "export", uiIcon: "empty",
|
||||
enabled: notSearch && noSelectedNotes },
|
||||
{ title: "Import into note", cmd: "importIntoNote", uiIcon: "empty",
|
||||
enabled: notSearch && noSelectedNotes },
|
||||
{ title: "Advanced", uiIcon: "empty", enabled: true, items: [
|
||||
{ title: 'Collapse subtree <kbd data-kb-action="CollapseSubtree"></kbd>', cmd: "collapseSubtree", uiIcon: "align-justify", enabled: noSelectedNotes },
|
||||
{ title: "Force note sync", cmd: "forceNoteSync", uiIcon: "recycle", enabled: noSelectedNotes },
|
||||
{ title: 'Sort alphabetically <kbd data-kb-action="SortChildNotes"></kbd>', cmd: "sortAlphabetically", uiIcon: "empty", enabled: noSelectedNotes && notSearch }
|
||||
] },
|
||||
enabled: notSearch && noSelectedNotes }
|
||||
].filter(row => row !== null);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,10 @@ import utils from './utils.js';
|
||||
import hoistedNoteService from './hoisted_note.js';
|
||||
import treeCache from "./tree_cache.js";
|
||||
|
||||
const $tree = $("#tree");
|
||||
|
||||
async function getParentProtectedStatus(node) {
|
||||
return await hoistedNoteService.isRootNode(node) ? 0 : node.getParent().data.isProtected;
|
||||
}
|
||||
|
||||
function getNodeByKey(key) {
|
||||
return $tree.fancytree('getNodeByKey', key);
|
||||
}
|
||||
|
||||
function getNoteIdFromNotePath(notePath) {
|
||||
if (!notePath) {
|
||||
return null;
|
||||
@@ -123,7 +117,6 @@ async function getNotePathTitle(notePath) {
|
||||
|
||||
export default {
|
||||
getParentProtectedStatus,
|
||||
getNodeByKey,
|
||||
getNotePath,
|
||||
getNoteIdFromNotePath,
|
||||
getNoteIdAndParentIdFromNotePath,
|
||||
|
||||
@@ -209,9 +209,50 @@ function getMimeTypeClass(mime) {
|
||||
function closeActiveDialog() {
|
||||
if (glob.activeDialog) {
|
||||
glob.activeDialog.modal('hide');
|
||||
glob.activeDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
let $lastFocusedElement = null;
|
||||
|
||||
function saveFocusedElement() {
|
||||
$lastFocusedElement = $(":focus");
|
||||
}
|
||||
|
||||
function focusSavedElement() {
|
||||
if (!$lastFocusedElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($lastFocusedElement.hasClass("ck")) {
|
||||
// must handle CKEditor separately because of this bug: https://github.com/ckeditor/ckeditor5/issues/607
|
||||
|
||||
import("./note_detail.js").then(noteDetail => {
|
||||
noteDetail.default.getActiveEditor().editing.view.focus();
|
||||
});
|
||||
} else {
|
||||
$lastFocusedElement.focus();
|
||||
}
|
||||
|
||||
$lastFocusedElement = null;
|
||||
}
|
||||
|
||||
function openDialog($dialog) {
|
||||
closeActiveDialog();
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
saveFocusedElement();
|
||||
|
||||
$dialog.modal();
|
||||
|
||||
$dialog.on('hidden.bs.modal', () => {
|
||||
if (!glob.activeDialog || glob.activeDialog === $dialog) {
|
||||
focusSavedElement();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isHtmlEmpty(html) {
|
||||
html = html.toLowerCase();
|
||||
|
||||
@@ -241,6 +282,17 @@ function getUrlForDownload(url) {
|
||||
}
|
||||
}
|
||||
|
||||
function copySelectionToClipboard() {
|
||||
const text = window.getSelection().toString();
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text);
|
||||
}
|
||||
}
|
||||
|
||||
function isCKEditorInitialized() {
|
||||
return !!(window && window.cutToNote);
|
||||
}
|
||||
|
||||
export default {
|
||||
reloadApp,
|
||||
parseDate,
|
||||
@@ -270,8 +322,13 @@ export default {
|
||||
getNoteTypeClass,
|
||||
getMimeTypeClass,
|
||||
closeActiveDialog,
|
||||
openDialog,
|
||||
saveFocusedElement,
|
||||
focusSavedElement,
|
||||
isHtmlEmpty,
|
||||
clearBrowserCache,
|
||||
getUrlForDownload,
|
||||
normalizeShortcut
|
||||
normalizeShortcut,
|
||||
copySelectionToClipboard,
|
||||
isCKEditorInitialized
|
||||
};
|
||||
@@ -71,7 +71,19 @@ class AttributesWidget extends StandardWidget {
|
||||
async renderAttributes(attributes, $container) {
|
||||
for (const attribute of attributes) {
|
||||
if (attribute.type === 'label') {
|
||||
$container.append(utils.formatLabel(attribute) + " ");
|
||||
if (attribute.name === 'externalLink') {
|
||||
$container.append('@' + attribute.name + "=");
|
||||
$container.append(
|
||||
$('<a>')
|
||||
.text(attribute.value)
|
||||
.attr('href', attribute.value)
|
||||
.addClass('external')
|
||||
);
|
||||
$container.append(" ");
|
||||
}
|
||||
else {
|
||||
$container.append(utils.formatLabel(attribute) + " ");
|
||||
}
|
||||
} else if (attribute.type === 'relation') {
|
||||
if (attribute.value) {
|
||||
$container.append('@' + attribute.name + "=");
|
||||
|
||||
@@ -41,7 +41,7 @@ class CalendarWidget extends StandardWidget {
|
||||
}
|
||||
|
||||
init($el, activeDate) {
|
||||
this.activeDate = new Date(Date.parse(activeDate));
|
||||
this.activeDate = new Date(activeDate + "T12:00:00"); // attaching time fixes local timezone handling
|
||||
this.todaysDate = new Date();
|
||||
this.date = new Date(this.activeDate.getTime());
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ body {
|
||||
min-height: 0;
|
||||
padding-left: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#search-box {
|
||||
|
||||
69
src/public/stylesheets/detail.css
Normal file
69
src/public/stylesheets/detail.css
Normal file
@@ -0,0 +1,69 @@
|
||||
.note-detail-book {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.note-detail-book-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.note-book-card {
|
||||
border-radius: 10px;
|
||||
background-color: var(--accented-background-color);
|
||||
padding: 15px;
|
||||
padding-bottom: 5px;
|
||||
margin: 5px;
|
||||
margin-left: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.note-book-card .note-book-card {
|
||||
border: 1px solid var(--main-border-color);
|
||||
}
|
||||
|
||||
.note-book-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.note-book-card.type-image .note-book-content, .note-book-card.type-file .note-book-content, .note-book-card.type-protected-session .note-book-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note-book-card.type-image .note-book-content img, .note-book-card.type-text .note-book-content img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.note-book-title {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.note-book-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.note-book-auto-message {
|
||||
background-color: var(--accented-background-color);
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.note-detail-image {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note-detail-image-view {
|
||||
max-width: 100%;
|
||||
}
|
||||
@@ -115,4 +115,5 @@ span.fancytree-expander {
|
||||
/* large left padding is necessary for ckeditor gutter in detail-only (smartphone) layout */
|
||||
padding-left: 35px;
|
||||
padding-top: 10px;
|
||||
min-height: 150px !important;
|
||||
}
|
||||
11
src/public/stylesheets/print.css
Normal file
11
src/public/stylesheets/print.css
Normal file
@@ -0,0 +1,11 @@
|
||||
@media print
|
||||
{
|
||||
.no-print, .no-print *
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.relation-map-wrapper {
|
||||
height: 100vh !important;
|
||||
}
|
||||
}
|
||||
@@ -518,14 +518,6 @@ button.icon-button {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.note-detail-image {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note-detail-image-view {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
pre:not(.CodeMirror-line) {
|
||||
color: var(--main-text-color) !important;
|
||||
white-space: pre-wrap;
|
||||
@@ -857,68 +849,6 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.note-detail-book {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.note-detail-book-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.note-book-card {
|
||||
border-radius: 10px;
|
||||
background-color: var(--accented-background-color);
|
||||
padding: 15px;
|
||||
padding-bottom: 5px;
|
||||
margin: 5px;
|
||||
margin-left: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.note-book-card .note-book-card {
|
||||
border: 1px solid var(--main-border-color);
|
||||
}
|
||||
|
||||
.note-book-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.note-book-card.type-image .note-book-content, .note-book-card.type-file .note-book-content, .note-book-card.type-protected-session .note-book-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note-book-card.type-image .note-book-content img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.note-book-title {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.note-book-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.note-book-auto-message {
|
||||
background-color: var(--accented-background-color);
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#toast-container {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
const app_info = require('../../services/app_info');
|
||||
const appInfo = require('../../services/app_info');
|
||||
|
||||
async function getAppInfo() {
|
||||
return app_info;
|
||||
return appInfo;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -101,7 +101,8 @@ async function deleteBranch(req) {
|
||||
const branch = await repository.getBranch(req.params.branchId);
|
||||
const taskContext = TaskContext.getInstance(req.query.taskId, 'delete-notes');
|
||||
|
||||
const noteDeleted = await notes.deleteBranch(branch, taskContext);
|
||||
const deleteId = utils.randomString(10);
|
||||
const noteDeleted = await notes.deleteBranch(branch, deleteId, taskContext);
|
||||
|
||||
if (last) {
|
||||
taskContext.taskSucceeded();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
const attributeService = require("../../services/attributes");
|
||||
const noteService = require('../../services/notes');
|
||||
const dateNoteService = require('../../services/date_notes');
|
||||
const dateUtils = require('../../services/date_utils');
|
||||
@@ -23,16 +24,26 @@ async function findClippingNote(todayNote, pageUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getClipperInboxNote() {
|
||||
let clipperInbox = await attributeService.getNoteWithLabel('clipperInbox');
|
||||
|
||||
if (!clipperInbox) {
|
||||
clipperInbox = await dateNoteService.getDateNote(dateUtils.localNowDate());
|
||||
}
|
||||
|
||||
return clipperInbox;
|
||||
}
|
||||
|
||||
async function addClipping(req) {
|
||||
const {title, content, pageUrl, images} = req.body;
|
||||
|
||||
const todayNote = await dateNoteService.getDateNote(dateUtils.localNowDate());
|
||||
const clipperInbox = await getClipperInboxNote();
|
||||
|
||||
let clippingNote = await findClippingNote(todayNote, pageUrl);
|
||||
let clippingNote = await findClippingNote(clipperInbox, pageUrl);
|
||||
|
||||
if (!clippingNote) {
|
||||
clippingNote = (await noteService.createNewNote({
|
||||
parentNoteId: todayNote.noteId,
|
||||
parentNoteId: clipperInbox.noteId,
|
||||
title: title,
|
||||
content: '',
|
||||
type: 'text'
|
||||
@@ -54,10 +65,10 @@ async function addClipping(req) {
|
||||
async function createNote(req) {
|
||||
const {title, content, pageUrl, images, clipType} = req.body;
|
||||
|
||||
const todayNote = await dateNoteService.getDateNote(dateUtils.localNowDate());
|
||||
const clipperInbox = await getClipperInboxNote();
|
||||
|
||||
const {note} = await noteService.createNewNote({
|
||||
parentNoteId: todayNote.noteId,
|
||||
parentNoteId: clipperInbox.noteId,
|
||||
title,
|
||||
content,
|
||||
type: 'text'
|
||||
|
||||
@@ -49,7 +49,7 @@ async function loginSync(req) {
|
||||
|
||||
return {
|
||||
sourceId: sourceIdService.getCurrentSourceId(),
|
||||
maxSyncId: await sql.getValue("SELECT MAX(id) FROM sync")
|
||||
maxSyncId: await sql.getValue("SELECT MAX(id) FROM sync WHERE isSynced = 1")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -55,12 +55,15 @@ async function deleteNote(req) {
|
||||
const taskId = req.query.taskId;
|
||||
const last = req.query.last === 'true';
|
||||
|
||||
// note how deleteId is separate from taskId - single taskId produces separate deleteId for each "top level" deleted note
|
||||
const deleteId = utils.randomString(10);
|
||||
|
||||
const note = await repository.getNote(noteId);
|
||||
|
||||
const taskContext = TaskContext.getInstance(taskId, 'delete-notes');
|
||||
|
||||
for (const branch of await note.getBranches()) {
|
||||
await noteService.deleteBranch(branch, taskContext);
|
||||
await noteService.deleteBranch(branch, deleteId, taskContext);
|
||||
}
|
||||
|
||||
if (last) {
|
||||
@@ -68,6 +71,16 @@ async function deleteNote(req) {
|
||||
}
|
||||
}
|
||||
|
||||
async function undeleteNote(req) {
|
||||
const note = await repository.getNote(req.params.noteId);
|
||||
|
||||
const taskContext = TaskContext.getInstance(utils.randomString(10), 'undelete-notes');
|
||||
|
||||
await noteService.undeleteNote(note, note.deleteId, taskContext);
|
||||
|
||||
await taskContext.taskSucceeded();
|
||||
}
|
||||
|
||||
async function sortNotes(req) {
|
||||
const noteId = req.params.noteId;
|
||||
|
||||
@@ -169,6 +182,7 @@ module.exports = {
|
||||
getNote,
|
||||
updateNote,
|
||||
deleteNote,
|
||||
undeleteNote,
|
||||
createNote,
|
||||
sortNotes,
|
||||
protectSubtree,
|
||||
|
||||
@@ -6,6 +6,7 @@ const attributes = require('../../services/attributes');
|
||||
|
||||
// options allowed to be updated directly in options dialog
|
||||
const ALLOWED_OPTIONS = new Set([
|
||||
'eraseNotesAfterTimeInSeconds',
|
||||
'protectedSessionTimeout',
|
||||
'noteRevisionSnapshotTimeInterval',
|
||||
'zoomFactor',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
const sql = require('../../services/sql');
|
||||
const protectedSessionService = require('../../services/protected_session');
|
||||
const noteService = require('../../services/notes');
|
||||
|
||||
async function getRecentChanges() {
|
||||
const recentChanges = await sql.getRows(
|
||||
@@ -10,6 +11,8 @@ async function getRecentChanges() {
|
||||
SELECT
|
||||
notes.noteId,
|
||||
notes.isDeleted AS current_isDeleted,
|
||||
notes.deleteId AS current_deleteId,
|
||||
notes.isErased AS current_isErased,
|
||||
notes.title AS current_title,
|
||||
notes.isProtected AS current_isProtected,
|
||||
note_revisions.title,
|
||||
@@ -19,21 +22,23 @@ async function getRecentChanges() {
|
||||
JOIN notes USING(noteId)
|
||||
ORDER BY
|
||||
note_revisions.utcDateCreated DESC
|
||||
LIMIT 1000
|
||||
LIMIT 200
|
||||
)
|
||||
UNION ALL SELECT * FROM (
|
||||
SELECT
|
||||
notes.noteId,
|
||||
notes.isDeleted AS current_isDeleted,
|
||||
notes.deleteId AS current_deleteId,
|
||||
notes.isErased AS current_isErased,
|
||||
notes.title AS current_title,
|
||||
notes.isProtected AS current_isProtected,
|
||||
notes.title,
|
||||
notes.utcDateCreated AS date
|
||||
notes.utcDateModified AS date
|
||||
FROM
|
||||
notes
|
||||
ORDER BY
|
||||
utcDateCreated DESC
|
||||
LIMIT 1000
|
||||
utcDateModified DESC
|
||||
LIMIT 200
|
||||
)
|
||||
ORDER BY date DESC
|
||||
LIMIT 200`);
|
||||
@@ -48,6 +53,20 @@ async function getRecentChanges() {
|
||||
change.title = change.current_title = "[Protected]";
|
||||
}
|
||||
}
|
||||
|
||||
if (change.current_isDeleted) {
|
||||
if (change.current_isErased) {
|
||||
change.canBeUndeleted = false;
|
||||
}
|
||||
else {
|
||||
const deleteId = change.current_deleteId;
|
||||
|
||||
const undeletedParentBranches = await noteService.getUndeletedParentBranches(change.noteId, deleteId);
|
||||
|
||||
// note (and the subtree) can be undeleted if there's at least one undeleted parent (whose branch would be undeleted by this op)
|
||||
change.canBeUndeleted = undeletedParentBranches.length > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return recentChanges;
|
||||
|
||||
@@ -29,8 +29,12 @@ async function searchFromNote(req) {
|
||||
return [404, `Note ${req.params.noteId} has not been found.`];
|
||||
}
|
||||
|
||||
if (note.isDeleted) {
|
||||
return [400, `Note ${req.params.noteId} is deleted.`];
|
||||
}
|
||||
|
||||
if (note.type !== 'search') {
|
||||
return [400, '`Note ${req.params.noteId} is not search note.`']
|
||||
return [400, `Note ${req.params.noteId} is not search note.`]
|
||||
}
|
||||
|
||||
const json = await note.getJsonContent();
|
||||
@@ -41,18 +45,28 @@ async function searchFromNote(req) {
|
||||
|
||||
let noteIds;
|
||||
|
||||
if (json.searchString.startsWith('=')) {
|
||||
const relationName = json.searchString.substr(1).trim();
|
||||
try {
|
||||
if (json.searchString.startsWith('=')) {
|
||||
const relationName = json.searchString.substr(1).trim();
|
||||
|
||||
noteIds = await searchFromRelation(note, relationName);
|
||||
noteIds = await searchFromRelation(note, relationName);
|
||||
} else {
|
||||
noteIds = await searchService.searchForNoteIds(json.searchString);
|
||||
}
|
||||
}
|
||||
else {
|
||||
noteIds = await searchService.searchForNoteIds(json.searchString);
|
||||
catch (e) {
|
||||
log.error(`Search failed for note ${note.noteId}: ` + e.message + ": " + e.stack);
|
||||
|
||||
throw new Error("Search failed, see logs for details.");
|
||||
}
|
||||
|
||||
// we won't return search note's own noteId
|
||||
noteIds = noteIds.filter(noteId => noteId !== note.noteId);
|
||||
|
||||
if (noteIds.length > 200) {
|
||||
noteIds = noteIds.slice(0, 200);
|
||||
}
|
||||
|
||||
return noteIds.map(noteCacheService.getNotePath).filter(res => !!res);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ async function getStats() {
|
||||
async function checkSync() {
|
||||
return {
|
||||
entityHashes: await contentHashService.getEntityHashes(),
|
||||
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync')
|
||||
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync WHERE isSynced = 1')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -116,11 +116,11 @@ async function forceNoteSync(req) {
|
||||
async function getChanged(req) {
|
||||
const lastSyncId = parseInt(req.query.lastSyncId);
|
||||
|
||||
const syncs = await sql.getRows("SELECT * FROM sync WHERE id > ? LIMIT 1000", [lastSyncId]);
|
||||
const syncs = await sql.getRows("SELECT * FROM sync WHERE isSynced = 1 AND id > ? LIMIT 1000", [lastSyncId]);
|
||||
|
||||
return {
|
||||
syncs: await syncService.getSyncRecords(syncs),
|
||||
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync')
|
||||
maxSyncId: await sql.getValue('SELECT MAX(id) FROM sync WHERE isSynced = 1')
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ function register(app) {
|
||||
apiRoute(GET, '/api/notes/:noteId', notesApiRoute.getNote);
|
||||
apiRoute(PUT, '/api/notes/:noteId', notesApiRoute.updateNote);
|
||||
apiRoute(DELETE, '/api/notes/:noteId', notesApiRoute.deleteNote);
|
||||
apiRoute(PUT, '/api/notes/:noteId/undelete', notesApiRoute.undeleteNote);
|
||||
apiRoute(POST, '/api/notes/:parentNoteId/children', notesApiRoute.createNote);
|
||||
apiRoute(PUT, '/api/notes/:noteId/sort', notesApiRoute.sortNotes);
|
||||
apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectSubtree);
|
||||
|
||||
@@ -4,8 +4,8 @@ const build = require('./build');
|
||||
const packageJson = require('../../package');
|
||||
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
||||
|
||||
const APP_DB_VERSION = 155;
|
||||
const SYNC_VERSION = 13;
|
||||
const APP_DB_VERSION = 158;
|
||||
const SYNC_VERSION = 14;
|
||||
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = { buildDate:"2020-01-08T21:01:24+01:00", buildRevision: "2b69abf8ab2241f01cd38b31308e54b9faaa74d5" };
|
||||
module.exports = { buildDate:"2020-03-28T20:58:20+01:00", buildRevision: "b4b22d9353bdc6ad4d3bab7cdb33bd0b844cc36d" };
|
||||
|
||||
@@ -7,6 +7,7 @@ const noteService = require('./notes');
|
||||
const repository = require('./repository');
|
||||
const Branch = require('../entities/branch');
|
||||
const TaskContext = require("./task_context.js");
|
||||
const utils = require('./utils');
|
||||
|
||||
async function cloneNoteToParent(noteId, parentNoteId, prefix) {
|
||||
if (await isNoteDeleted(noteId) || await isNoteDeleted(parentNoteId)) {
|
||||
@@ -54,7 +55,8 @@ async function ensureNoteIsAbsentFromParent(noteId, parentNoteId) {
|
||||
const branch = await repository.getEntity(`SELECT * FROM branches WHERE noteId = ? AND parentNoteId = ? AND isDeleted = 0`, [noteId, parentNoteId]);
|
||||
|
||||
if (branch) {
|
||||
await noteService.deleteBranch(branch, new TaskContext());
|
||||
const deleteId = utils.randomString(10);
|
||||
await noteService.deleteBranch(branch, deleteId, new TaskContext());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class ConsistencyChecks {
|
||||
childToParents[childNoteId].push(parentNoteId);
|
||||
}
|
||||
|
||||
function checkTreeCycle(noteId, path) {
|
||||
const checkTreeCycle = (noteId, path) => {
|
||||
if (noteId === 'root') {
|
||||
return;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class ConsistencyChecks {
|
||||
checkTreeCycle(parentNoteId, newPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const noteIds = Object.keys(childToParents);
|
||||
|
||||
@@ -323,14 +323,25 @@ class ConsistencyChecks {
|
||||
WHERE isErased = 1
|
||||
AND content IS NOT NULL`,
|
||||
async ({noteId}) => {
|
||||
if (this.autoFix) {
|
||||
await sql.execute(`UPDATE note_contents SET content = NULL WHERE noteId = ?`, [noteId]);
|
||||
|
||||
logFix(`Note ${noteId} content has been set to null since the note is erased`);
|
||||
}
|
||||
else {
|
||||
logError(`Note ${noteId} content is not null even though the note is erased`);
|
||||
}
|
||||
// we always fix this issue because there does not seem to be a good way to prevent it.
|
||||
// Scenario in which this can happen:
|
||||
// 1. user on instance A deletes the note (sync for notes is created, but not for note_contents) and is later erased
|
||||
// 2. instance B gets synced from instance A, note is updated because of sync row for notes,
|
||||
// but note_contents is not because erasion does not create sync rows
|
||||
// 3. therefore note.isErased = true, but note_contents.content remains not updated and not erased.
|
||||
//
|
||||
// Considered solutions:
|
||||
// - don't sync erased notes - this might prevent syncing also of the isDeleted flag and note would continue
|
||||
// to exist on the other instance
|
||||
// - create sync rows for erased event - this would be a problem for undeletion since erasion might happen
|
||||
// on one instance after undelete and thus would win even though there's no user action behind it
|
||||
//
|
||||
// So instead we just fix such cases afterwards here.
|
||||
|
||||
await sql.execute(`UPDATE note_contents SET content = NULL WHERE noteId = ?`, [noteId]);
|
||||
|
||||
logFix(`Note ${noteId} content has been set to null since the note is erased`);
|
||||
});
|
||||
|
||||
await this.findAndFixIssues(`
|
||||
@@ -535,7 +546,7 @@ class ConsistencyChecks {
|
||||
${entityName}
|
||||
LEFT JOIN sync ON sync.entityName = '${entityName}' AND entityId = ${key}
|
||||
WHERE
|
||||
sync.id IS NULL AND ` + (entityName === 'options' ? 'isSynced = 1' : '1'),
|
||||
sync.id IS NULL AND ` + (entityName === 'options' ? 'options.isSynced = 1' : '1'),
|
||||
async ({entityId}) => {
|
||||
if (this.autoFix) {
|
||||
await syncTableService.addEntitySync(entityName, entityId);
|
||||
@@ -547,23 +558,23 @@ class ConsistencyChecks {
|
||||
});
|
||||
|
||||
await this.findAndFixIssues(`
|
||||
SELECT
|
||||
id, entityId
|
||||
FROM
|
||||
sync
|
||||
LEFT JOIN ${entityName} ON entityId = ${key}
|
||||
WHERE
|
||||
sync.entityName = '${entityName}'
|
||||
AND ${key} IS NULL`,
|
||||
async ({id, entityId}) => {
|
||||
if (this.autoFix) {
|
||||
await sql.execute("DELETE FROM sync WHERE entityName = ? AND entityId = ?", [entityName, entityId]);
|
||||
SELECT
|
||||
id, entityId
|
||||
FROM
|
||||
sync
|
||||
LEFT JOIN ${entityName} ON entityId = ${key}
|
||||
WHERE
|
||||
sync.entityName = '${entityName}'
|
||||
AND ${key} IS NULL`,
|
||||
async ({id, entityId}) => {
|
||||
if (this.autoFix) {
|
||||
await sql.execute("DELETE FROM sync WHERE entityName = ? AND entityId = ?", [entityName, entityId]);
|
||||
|
||||
logFix(`Deleted extra sync record id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
||||
} else {
|
||||
logError(`Unrecognized sync record id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
||||
}
|
||||
});
|
||||
logFix(`Deleted extra sync record id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
||||
} else {
|
||||
logError(`Unrecognized sync record id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async findSyncRowsIssues() {
|
||||
|
||||
@@ -40,7 +40,9 @@ async function getRootCalendarNote() {
|
||||
parentNoteId: 'root',
|
||||
title: 'Calendar',
|
||||
target: 'into',
|
||||
isProtected: false
|
||||
isProtected: false,
|
||||
type: 'text',
|
||||
content: ''
|
||||
})).note;
|
||||
|
||||
await attributeService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL);
|
||||
|
||||
@@ -16,7 +16,7 @@ async function exportToOpml(taskContext, branch, version, res) {
|
||||
const branch = await repository.getBranch(branchId);
|
||||
const note = await branch.getNote();
|
||||
|
||||
if (!note.isStringNote() || await note.hasOwnedLabel('excludeFromExport')) {
|
||||
if (await note.hasOwnedLabel('excludeFromExport')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ async function exportToOpml(taskContext, branch, version, res) {
|
||||
|
||||
if (opmlVersion === 1) {
|
||||
const preparedTitle = escapeXmlAttribute(title);
|
||||
const preparedContent = prepareText(await note.getContent());
|
||||
const preparedContent = note.isStringNote() ? prepareText(await note.getContent()) : '';
|
||||
|
||||
res.write(`<outline title="${preparedTitle}" text="${preparedContent}">\n`);
|
||||
}
|
||||
else if (opmlVersion === 2) {
|
||||
const preparedTitle = escapeXmlAttribute(title);
|
||||
const preparedContent = escapeXmlAttribute(await note.getContent());
|
||||
const preparedContent = note.isStringNote() ? escapeXmlAttribute(await note.getContent()) : '';
|
||||
|
||||
res.write(`<outline text="${preparedTitle}" _note="${preparedContent}">\n`);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const sax = require("sax");
|
||||
const fileType = require('file-type');
|
||||
const FileType = require('file-type');
|
||||
const stream = require('stream');
|
||||
const log = require("../log");
|
||||
const utils = require("../utils");
|
||||
@@ -235,14 +235,12 @@ async function importEnex(taskContext, file, parentNote) {
|
||||
|
||||
taskContext.increaseProgressCount();
|
||||
|
||||
let noteContent = await noteEntity.getContent();
|
||||
|
||||
for (const resource of resources) {
|
||||
const hash = utils.md5(resource.content);
|
||||
|
||||
const mediaRegex = new RegExp(`<en-media hash="${hash}"[^>]*>`, 'g');
|
||||
|
||||
const fileTypeFromBuffer = fileType(resource.content);
|
||||
const fileTypeFromBuffer = await FileType.fromBuffer(resource.content);
|
||||
if (fileTypeFromBuffer) {
|
||||
// If fileType returns something for buffer, then set the mime given
|
||||
resource.mime = fileTypeFromBuffer.mime;
|
||||
@@ -268,7 +266,7 @@ async function importEnex(taskContext, file, parentNote) {
|
||||
|
||||
const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`;
|
||||
|
||||
noteContent = noteContent.replace(mediaRegex, resourceLink);
|
||||
content = content.replace(mediaRegex, resourceLink);
|
||||
};
|
||||
|
||||
if (["image/jpeg", "image/png", "image/gif", "image/webp"].includes(resource.mime)) {
|
||||
@@ -281,12 +279,12 @@ async function importEnex(taskContext, file, parentNote) {
|
||||
|
||||
const imageLink = `<img src="${url}">`;
|
||||
|
||||
noteContent = noteContent.replace(mediaRegex, imageLink);
|
||||
content = content.replace(mediaRegex, imageLink);
|
||||
|
||||
if (!noteContent.includes(imageLink)) {
|
||||
if (!content.includes(imageLink)) {
|
||||
// if there wasn't any match for the reference, we'll add the image anyway
|
||||
// otherwise image would be removed since no note would include it
|
||||
noteContent += imageLink;
|
||||
content += imageLink;
|
||||
}
|
||||
} catch (e) {
|
||||
log.error("error when saving image from ENEX file: " + e);
|
||||
@@ -298,7 +296,7 @@ async function importEnex(taskContext, file, parentNote) {
|
||||
}
|
||||
|
||||
// save updated content with links to files/images
|
||||
await noteEntity.setContent(noteContent);
|
||||
await noteEntity.setContent(content);
|
||||
|
||||
await noteService.scanForLinks(noteEntity.noteId);
|
||||
|
||||
|
||||
@@ -147,11 +147,16 @@ async function importTar(taskContext, fileBuffer, importRootNote) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attr.type === 'relation' && ['internalLink', 'imageLink', 'relationMapLink'].includes(attr.name)) {
|
||||
if (attr.type === 'relation' && ['internalLink', 'imageLink', 'relationMapLink', 'includeNoteLink'].includes(attr.name)) {
|
||||
// these relations are created automatically and as such don't need to be duplicated in the import
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attr.type === 'label' && attr.name === 'externalLink') {
|
||||
// also created automatically
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attr.type === 'relation') {
|
||||
attr.value = getNewNoteId(attr.value);
|
||||
}
|
||||
|
||||
@@ -306,6 +306,10 @@ const DEFAULT_KEYBOARD_ACTIONS = [
|
||||
{
|
||||
actionName: "ZoomIn",
|
||||
defaultShortcuts: ["CommandOrControl+="]
|
||||
},
|
||||
{
|
||||
actionName: "CopyWithoutFormatting",
|
||||
defaultShortcuts: ["CommandOrControl+Alt+C"]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -242,6 +242,34 @@ function findInternalLinks(content, foundLinks) {
|
||||
return content.replace(/href="[^"]*#root/g, 'href="#root');
|
||||
}
|
||||
|
||||
function findExternalLinks(content, foundLinks) {
|
||||
const re = /href="([a-zA-Z]+:\/\/[^"]*)"/g;
|
||||
let match;
|
||||
|
||||
while (match = re.exec(content)) {
|
||||
foundLinks.push({
|
||||
name: 'externalLink',
|
||||
value: match[1]
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
function findIncludeNoteLinks(content, foundLinks) {
|
||||
const re = /<section class="include-note" data-note-id="([a-zA-Z0-9]+)">/g;
|
||||
let match;
|
||||
|
||||
while (match = re.exec(content)) {
|
||||
foundLinks.push({
|
||||
name: 'includeNoteLink',
|
||||
value: match[1]
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
function findRelationMapLinks(content, foundLinks) {
|
||||
const obj = JSON.parse(content);
|
||||
|
||||
@@ -267,6 +295,8 @@ async function saveLinks(note, content) {
|
||||
if (note.type === 'text') {
|
||||
content = findImageLinks(content, foundLinks);
|
||||
content = findInternalLinks(content, foundLinks);
|
||||
content = findExternalLinks(content, foundLinks);
|
||||
content = findIncludeNoteLinks(content, foundLinks);
|
||||
}
|
||||
else if (note.type === 'relation-map') {
|
||||
findRelationMapLinks(content, foundLinks);
|
||||
@@ -278,9 +308,11 @@ async function saveLinks(note, content) {
|
||||
const existingLinks = await note.getLinks();
|
||||
|
||||
for (const foundLink of foundLinks) {
|
||||
const targetNote = await repository.getNote(foundLink.value);
|
||||
if (!targetNote || targetNote.isDeleted) {
|
||||
continue;
|
||||
if (foundLink.name !== 'externalLink') {
|
||||
const targetNote = await repository.getNote(foundLink.value);
|
||||
if (!targetNote || targetNote.isDeleted) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const existingLink = existingLinks.find(existingLink =>
|
||||
@@ -290,7 +322,7 @@ async function saveLinks(note, content) {
|
||||
if (!existingLink) {
|
||||
await new Attribute({
|
||||
noteId: note.noteId,
|
||||
type: 'relation',
|
||||
type: foundLink.name === 'externalLink' ? 'label' : 'relation',
|
||||
name: foundLink.name,
|
||||
value: foundLink.value,
|
||||
}).save();
|
||||
@@ -389,8 +421,14 @@ async function updateNote(noteId, noteUpdates) {
|
||||
};
|
||||
}
|
||||
|
||||
/** @return {boolean} - true if note has been deleted, false otherwise */
|
||||
async function deleteBranch(branch, taskContext) {
|
||||
/**
|
||||
* @param {Branch} branch
|
||||
* @param {string} deleteId
|
||||
* @param {TaskContext} taskContext
|
||||
*
|
||||
* @return {boolean} - true if note has been deleted, false otherwise
|
||||
*/
|
||||
async function deleteBranch(branch, deleteId, taskContext) {
|
||||
taskContext.increaseProgressCount();
|
||||
|
||||
if (!branch || branch.isDeleted) {
|
||||
@@ -405,26 +443,32 @@ async function deleteBranch(branch, taskContext) {
|
||||
}
|
||||
|
||||
branch.isDeleted = true;
|
||||
branch.deleteId = deleteId;
|
||||
await branch.save();
|
||||
|
||||
const note = await branch.getNote();
|
||||
const notDeletedBranches = await note.getBranches();
|
||||
|
||||
if (notDeletedBranches.length === 0) {
|
||||
note.isDeleted = true;
|
||||
await note.save();
|
||||
|
||||
for (const childBranch of await note.getChildBranches()) {
|
||||
await deleteBranch(childBranch, taskContext);
|
||||
await deleteBranch(childBranch, deleteId, taskContext);
|
||||
}
|
||||
|
||||
// first delete children and then parent - this will show up better in recent changes
|
||||
|
||||
note.isDeleted = true;
|
||||
note.deleteId = deleteId;
|
||||
await note.save();
|
||||
|
||||
for (const attribute of await note.getOwnedAttributes()) {
|
||||
attribute.isDeleted = true;
|
||||
attribute.deleteId = deleteId;
|
||||
await attribute.save();
|
||||
}
|
||||
|
||||
for (const relation of await note.getTargetRelations()) {
|
||||
relation.isDeleted = true;
|
||||
relation.deleteId = deleteId;
|
||||
await relation.save();
|
||||
}
|
||||
|
||||
@@ -435,6 +479,88 @@ async function deleteBranch(branch, taskContext) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Note} note
|
||||
* @param {string} deleteId
|
||||
* @param {TaskContext} taskContext
|
||||
*/
|
||||
async function undeleteNote(note, deleteId, taskContext) {
|
||||
const undeletedParentBranches = await getUndeletedParentBranches(note.noteId, deleteId);
|
||||
|
||||
if (undeletedParentBranches.length === 0) {
|
||||
// cannot undelete if there's no undeleted parent
|
||||
return;
|
||||
}
|
||||
|
||||
for (const parentBranch of undeletedParentBranches) {
|
||||
await undeleteBranch(parentBranch, deleteId, taskContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Branch} branch
|
||||
* @param {string} deleteId
|
||||
* @param {TaskContext} taskContext
|
||||
*/
|
||||
async function undeleteBranch(branch, deleteId, taskContext) {
|
||||
if (!branch.isDeleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const note = await branch.getNote();
|
||||
|
||||
if (note.isDeleted && note.deleteId !== deleteId) {
|
||||
return;
|
||||
}
|
||||
|
||||
branch.isDeleted = false;
|
||||
await branch.save();
|
||||
|
||||
taskContext.increaseProgressCount();
|
||||
|
||||
if (note.isDeleted && note.deleteId === deleteId) {
|
||||
note.isDeleted = false;
|
||||
await note.save();
|
||||
|
||||
const attrs = await repository.getEntities(`
|
||||
SELECT * FROM attributes
|
||||
WHERE isDeleted = 1
|
||||
AND deleteId = ?
|
||||
AND (noteId = ?
|
||||
OR (type = 'relation' AND value = ?))`, [deleteId, note.noteId, note.noteId]);
|
||||
|
||||
for (const attr of attrs) {
|
||||
attr.isDeleted = false;
|
||||
await attr.save();
|
||||
}
|
||||
|
||||
const childBranches = await repository.getEntities(`
|
||||
SELECT branches.*
|
||||
FROM branches
|
||||
WHERE branches.isDeleted = 1
|
||||
AND branches.deleteId = ?
|
||||
AND branches.parentNoteId = ?`, [deleteId, note.noteId]);
|
||||
|
||||
for (const childBranch of childBranches) {
|
||||
await undeleteBranch(childBranch, deleteId, taskContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return return deleted branches of an undeleted parent note
|
||||
*/
|
||||
async function getUndeletedParentBranches(noteId, deleteId) {
|
||||
return await repository.getEntities(`
|
||||
SELECT branches.*
|
||||
FROM branches
|
||||
JOIN notes AS parentNote ON parentNote.noteId = branches.parentNoteId
|
||||
WHERE branches.noteId = ?
|
||||
AND branches.isDeleted = 1
|
||||
AND branches.deleteId = ?
|
||||
AND parentNote.isDeleted = 0`, [noteId, deleteId]);
|
||||
}
|
||||
|
||||
async function scanForLinks(noteId) {
|
||||
const note = await repository.getNote(noteId);
|
||||
if (!note || !['text', 'relation-map'].includes(note.type)) {
|
||||
@@ -453,7 +579,9 @@ async function scanForLinks(noteId) {
|
||||
}
|
||||
|
||||
async function eraseDeletedNotes() {
|
||||
const cutoffDate = new Date(Date.now() - 48 * 3600 * 1000);
|
||||
const eraseNotesAfterTimeInSeconds = await optionService.getOptionInt('eraseNotesAfterTimeInSeconds');
|
||||
|
||||
const cutoffDate = new Date(Date.now() - eraseNotesAfterTimeInSeconds * 1000);
|
||||
|
||||
const noteIdsToErase = await sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND isErased = 0 AND notes.utcDateModified <= ?", [dateUtils.utcDateStr(cutoffDate)]);
|
||||
|
||||
@@ -467,10 +595,12 @@ async function eraseDeletedNotes() {
|
||||
// - we don't want change the hash since this erasing happens on each instance separately
|
||||
// and changing the hash would fire up the sync errors temporarily
|
||||
|
||||
// setting contentLength to zero would serve no benefit and it leaves potentially useful trail
|
||||
await sql.executeMany(`
|
||||
UPDATE notes
|
||||
SET isErased = 1
|
||||
SET title = '[deleted]',
|
||||
contentLength = 0,
|
||||
isProtected = 0,
|
||||
isErased = 1
|
||||
WHERE noteId IN (???)`, noteIdsToErase);
|
||||
|
||||
await sql.executeMany(`
|
||||
@@ -488,9 +618,16 @@ async function eraseDeletedNotes() {
|
||||
await sql.executeMany(`
|
||||
UPDATE note_revisions
|
||||
SET isErased = 1,
|
||||
title = NULL
|
||||
title = NULL,
|
||||
contentLength = 0
|
||||
WHERE isErased = 0 AND noteId IN (???)`, noteIdsToErase);
|
||||
|
||||
await sql.executeMany(`
|
||||
UPDATE attributes
|
||||
SET name = 'deleted',
|
||||
value = ''
|
||||
WHERE noteId IN (???)`, noteIdsToErase);
|
||||
|
||||
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
|
||||
}
|
||||
|
||||
@@ -544,7 +681,9 @@ module.exports = {
|
||||
createNewNoteWithTarget,
|
||||
updateNote,
|
||||
deleteBranch,
|
||||
undeleteNote,
|
||||
protectNoteRecursively,
|
||||
scanForLinks,
|
||||
duplicateNote
|
||||
duplicateNote,
|
||||
getUndeletedParentBranches
|
||||
};
|
||||
|
||||
@@ -80,7 +80,8 @@ const defaultOptions = [
|
||||
{ name: 'leftPaneWidth', value: '25', isSynced: false },
|
||||
{ name: 'rightPaneWidth', value: '25', isSynced: false },
|
||||
{ name: 'rightPaneVisible', value: 'true', isSynced: false },
|
||||
{ name: 'nativeTitleBarVisible', value: 'false', isSynced: false }
|
||||
{ name: 'nativeTitleBarVisible', value: 'false', isSynced: false },
|
||||
{ name: 'eraseNotesAfterTimeInSeconds', value: '604800', isSynced: true } // default is 7 days
|
||||
];
|
||||
|
||||
async function initStartupOptions() {
|
||||
|
||||
@@ -2,7 +2,6 @@ const sql = require('./sql');
|
||||
const ScriptContext = require('./script_context');
|
||||
const repository = require('./repository');
|
||||
const cls = require('./cls');
|
||||
const sourceIdService = require('./source_id');
|
||||
const log = require('./log');
|
||||
|
||||
async function executeNote(note, apiParams) {
|
||||
|
||||
@@ -209,6 +209,8 @@ async function transactional(func) {
|
||||
|
||||
transactionActive = false;
|
||||
resolve();
|
||||
|
||||
setTimeout(() => require('./ws').sendPingToAllClients(), 50);
|
||||
}
|
||||
catch (e) {
|
||||
if (transactionActive) {
|
||||
|
||||
@@ -176,7 +176,7 @@ async function pushSync(syncContext) {
|
||||
let lastSyncedPush = await getLastSyncedPush();
|
||||
|
||||
while (true) {
|
||||
const syncs = await sql.getRows('SELECT * FROM sync WHERE id > ? LIMIT 1000', [lastSyncedPush]);
|
||||
const syncs = await sql.getRows('SELECT * FROM sync WHERE isSynced = 1 AND id > ? LIMIT 1000', [lastSyncedPush]);
|
||||
|
||||
if (syncs.length === 0) {
|
||||
log.info("Nothing to push");
|
||||
@@ -228,14 +228,15 @@ async function syncFinished(syncContext) {
|
||||
|
||||
async function checkContentHash(syncContext) {
|
||||
const resp = await syncRequest(syncContext, 'GET', '/api/sync/check');
|
||||
const lastSyncedPullId = await getLastSyncedPull();
|
||||
|
||||
if (await getLastSyncedPull() < resp.maxSyncId) {
|
||||
log.info("There are some outstanding pulls, skipping content check.");
|
||||
if (lastSyncedPullId < resp.maxSyncId) {
|
||||
log.info(`There are some outstanding pulls (${lastSyncedPullId} vs. ${resp.maxSyncId}), skipping content check.`);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const notPushedSyncs = await sql.getValue("SELECT EXISTS(SELECT 1 FROM sync WHERE id > ?)", [await getLastSyncedPush()]);
|
||||
const notPushedSyncs = await sql.getValue("SELECT EXISTS(SELECT 1 FROM sync WHERE isSynced = 1 AND id > ?)", [await getLastSyncedPush()]);
|
||||
|
||||
if (notPushedSyncs) {
|
||||
log.info(`There's ${notPushedSyncs} outstanding pushes, skipping content check.`);
|
||||
@@ -352,7 +353,7 @@ async function updatePushStats() {
|
||||
if (await syncOptions.isSyncSetup()) {
|
||||
const lastSyncedPush = await optionService.getOption('lastSyncedPush');
|
||||
|
||||
stats.outstandingPushes = await sql.getValue("SELECT COUNT(1) FROM sync WHERE id > ?", [lastSyncedPush]);
|
||||
stats.outstandingPushes = await sql.getValue("SELECT COUNT(1) FROM sync WHERE isSynced = 1 AND id > ?", [lastSyncedPush]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ async function insertEntitySync(entityName, entityId, sourceId) {
|
||||
entityName: entityName,
|
||||
entityId: entityId,
|
||||
utcSyncDate: dateUtils.utcNowDateTime(),
|
||||
sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId()
|
||||
sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId(),
|
||||
isSynced: 1
|
||||
};
|
||||
|
||||
sync.id = await sql.replace("sync", sync);
|
||||
@@ -23,8 +24,6 @@ async function addEntitySync(entityName, entityId, sourceId) {
|
||||
const sync = await insertEntitySync(entityName, entityId, sourceId);
|
||||
|
||||
syncs.push(sync);
|
||||
|
||||
setTimeout(() => require('./ws').sendPingToAllClients(), 50);
|
||||
}
|
||||
|
||||
async function addEntitySyncsForSector(entityName, entityPrimaryKey, sector) {
|
||||
|
||||
@@ -71,22 +71,36 @@ function sendMessageToAllClients(message) {
|
||||
}
|
||||
}
|
||||
|
||||
async function fillInAdditionalProperties(sync) {
|
||||
// fill in some extra data needed by the frontend
|
||||
if (sync.entityName === 'attributes') {
|
||||
sync.noteId = await sql.getValue(`SELECT noteId
|
||||
FROM attributes
|
||||
WHERE attributeId = ?`, [sync.entityId]);
|
||||
} else if (sync.entityName === 'note_revisions') {
|
||||
sync.noteId = await sql.getValue(`SELECT noteId
|
||||
FROM note_revisions
|
||||
WHERE noteRevisionId = ?`, [sync.entityId]);
|
||||
} else if (sync.entityName === 'branches') {
|
||||
const {noteId, parentNoteId} = await sql.getRow(`SELECT noteId, parentNoteId
|
||||
FROM branches
|
||||
WHERE branchId = ?`, [sync.entityId]);
|
||||
|
||||
sync.noteId = noteId;
|
||||
sync.parentNoteId = parentNoteId;
|
||||
}
|
||||
}
|
||||
|
||||
async function sendPing(client) {
|
||||
const syncData = require('./sync_table').getEntitySyncsNewerThan(lastAcceptedSyncIds[client.id]);
|
||||
|
||||
for (const sync of syncData) {
|
||||
// fill in some extra data needed by the frontend
|
||||
if (sync.entityName === 'attributes') {
|
||||
sync.noteId = await sql.getValue(`SELECT noteId FROM attributes WHERE attributeId = ?`, [sync.entityId]);
|
||||
try {
|
||||
await fillInAdditionalProperties(sync);
|
||||
}
|
||||
else if (sync.entityName === 'note_revisions') {
|
||||
sync.noteId = await sql.getValue(`SELECT noteId FROM note_revisions WHERE noteRevisionId = ?`, [sync.entityId]);
|
||||
}
|
||||
else if (sync.entityName === 'branches') {
|
||||
const {noteId, parentNoteId} = await sql.getRow(`SELECT noteId, parentNoteId FROM branches WHERE branchId = ?`, [sync.entityId]);
|
||||
|
||||
sync.noteId = noteId;
|
||||
sync.parentNoteId = parentNoteId;
|
||||
catch (e) {
|
||||
log.error("Could not fill additional properties for sync " + JSON.stringify(sync)
|
||||
+ " because of error: " + e.message + ": " + e.stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -250,6 +250,7 @@
|
||||
|
||||
<link href="stylesheets/themes.css" rel="stylesheet">
|
||||
<link href="stylesheets/style.css" rel="stylesheet">
|
||||
<link href="stylesheets/detail.css" rel="stylesheet">
|
||||
<link href="stylesheets/desktop.css" rel="stylesheet">
|
||||
|
||||
<script src="javascripts/desktop.js" crossorigin type="module"></script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="note-detail-image note-detail-component">
|
||||
<div style="display: flex; justify-content: space-evenly; margin: 10px;">
|
||||
<div class="no-print" style="display: flex; justify-content: space-evenly; margin: 10px;">
|
||||
<button class="image-download btn btn-sm btn-primary" type="button">Download</button>
|
||||
|
||||
<button class="image-copy-to-clipboard btn btn-sm btn-primary" type="button">Copy to clipboard</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="note-detail-relation-map note-detail-component">
|
||||
<button class="relation-map-create-child-note btn btn-sm floating-button" type="button"
|
||||
<button class="relation-map-create-child-note btn btn-sm floating-button no-print" type="button"
|
||||
title="Create new child note and add it into this relation map">
|
||||
<span class="bx bx-folder-plus"></span>
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
class="relation-map-reset-pan-zoom btn icon-button floating-button bx bx-crop"
|
||||
class="relation-map-reset-pan-zoom btn icon-button floating-button bx bx-crop no-print"
|
||||
title="Reset pan & zoom to initial coordinates and magnification"
|
||||
style="right: 70px;"></button>
|
||||
|
||||
<div class="btn-group floating-button" style="right: 10px;">
|
||||
<div class="btn-group floating-button no-print" style="right: 10px;">
|
||||
<button type="button"
|
||||
class="relation-map-zoom-in btn icon-button bx bx-zoom-in"
|
||||
title="Zoom In"></button>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<th>Homepage:</th>
|
||||
<td><a href="https://github.com/zadam/trilium" class="external">https://github.com/zadam/trilium</a></td>
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
<% include details/relation_map.ejs %>
|
||||
|
||||
<% include details/protected_session_password.ejs %>
|
||||
|
||||
<% include details/book.ejs %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,6 +118,7 @@
|
||||
|
||||
<link href="stylesheets/themes.css" rel="stylesheet">
|
||||
<link href="stylesheets/style.css" rel="stylesheet">
|
||||
<link href="stylesheets/detail.css" rel="stylesheet">
|
||||
<link href="stylesheets/mobile.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="libraries/boxicons/css/boxicons.min.css">
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
<label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType">
|
||||
I'm a new user and I want to create new Trilium document for my notes</label>
|
||||
</div>
|
||||
<div class="radio" data-bind="if: instanceType == 'server'" style="margin-bottom: 15px;">
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType">
|
||||
I have desktop instance already and I want to setup sync with it</label>
|
||||
</div>
|
||||
<div class="radio" data-bind="if: instanceType == 'desktop'" style="margin-bottom: 15px;">
|
||||
<div class="radio" style="margin-bottom: 15px;">
|
||||
<label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType">
|
||||
I have server instance up and I want to setup sync with it</label>
|
||||
I have server instance already and I want to setup sync with it</label>
|
||||
</div>
|
||||
|
||||
<button type="button" data-bind="disable: !setupTypeSelected(), click: selectSetupType" class="btn btn-primary">Next</button>
|
||||
|
||||
Reference in New Issue
Block a user