mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	note content refactoring, WIP
This commit is contained in:
		| @@ -363,324 +363,330 @@ parentNoteId</ColNames> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <DefaultExpression>NULL</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="87" parent="11" name="sqlite_autoindex_note_contents_1"> | ||||
|     <column id="87" parent="11" name="hash"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="88" parent="11" name="sqlite_autoindex_note_contents_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteContentId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="88" parent="11" name="IDX_note_contents_noteId"> | ||||
|     <index id="89" parent="11" name="IDX_note_contents_noteId"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="89" parent="11"> | ||||
|     <key id="90" parent="11"> | ||||
|       <ColNames>noteContentId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_note_contents_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="90" parent="12" name="noteRevisionId"> | ||||
|     <column id="91" parent="12" name="noteRevisionId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="91" parent="12" name="noteId"> | ||||
|     <column id="92" parent="12" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="92" parent="12" name="title"> | ||||
|     <column id="93" parent="12" name="title"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="93" parent="12" name="content"> | ||||
|     <column id="94" parent="12" name="content"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="94" parent="12" name="isProtected"> | ||||
|     <column id="95" parent="12" name="isProtected"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="95" parent="12" name="dateModifiedFrom"> | ||||
|     <column id="96" parent="12" name="dateModifiedFrom"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="96" parent="12" name="dateModifiedTo"> | ||||
|     <column id="97" parent="12" name="dateModifiedTo"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="97" parent="12" name="type"> | ||||
|     <column id="98" parent="12" name="type"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="98" parent="12" name="mime"> | ||||
|     <column id="99" parent="12" name="mime"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="99" parent="12" name="hash"> | ||||
|     <column id="100" parent="12" name="hash"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="100" parent="12" name="sqlite_autoindex_note_revisions_1"> | ||||
|     <index id="101" parent="12" name="sqlite_autoindex_note_revisions_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteRevisionId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="101" parent="12" name="IDX_note_revisions_noteId"> | ||||
|     <index id="102" parent="12" name="IDX_note_revisions_noteId"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="102" parent="12" name="IDX_note_revisions_dateModifiedFrom"> | ||||
|     <index id="103" parent="12" name="IDX_note_revisions_dateModifiedFrom"> | ||||
|       <ColNames>dateModifiedFrom</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="103" parent="12" name="IDX_note_revisions_dateModifiedTo"> | ||||
|     <index id="104" parent="12" name="IDX_note_revisions_dateModifiedTo"> | ||||
|       <ColNames>dateModifiedTo</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="104" parent="12"> | ||||
|     <key id="105" parent="12"> | ||||
|       <ColNames>noteRevisionId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="105" parent="13" name="noteId"> | ||||
|     <column id="106" parent="13" name="noteId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="106" parent="13" name="title"> | ||||
|     <column id="107" parent="13" name="title"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>"note"</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="107" parent="13" name="isProtected"> | ||||
|     <column id="108" parent="13" name="isProtected"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="108" parent="13" name="type"> | ||||
|     <column id="109" parent="13" name="type"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'text'</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="109" parent="13" name="mime"> | ||||
|     <column id="110" parent="13" name="mime"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'text/html'</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="110" parent="13" name="hash"> | ||||
|     <column id="111" parent="13" name="hash"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="111" parent="13" name="isDeleted"> | ||||
|     <column id="112" parent="13" name="isDeleted"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="112" parent="13" name="dateCreated"> | ||||
|     <column id="113" parent="13" name="dateCreated"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="113" parent="13" name="dateModified"> | ||||
|     <column id="114" parent="13" name="dateModified"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="114" parent="13" name="sqlite_autoindex_notes_1"> | ||||
|     <index id="115" parent="13" name="sqlite_autoindex_notes_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="115" parent="13" name="IDX_notes_isDeleted"> | ||||
|     <index id="116" parent="13" name="IDX_notes_isDeleted"> | ||||
|       <ColNames>isDeleted</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="116" parent="13"> | ||||
|     <key id="117" parent="13"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="117" parent="14" name="name"> | ||||
|     <column id="118" parent="14" name="name"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="118" parent="14" name="value"> | ||||
|     <column id="119" parent="14" name="value"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="119" parent="14" name="dateModified"> | ||||
|     <column id="120" parent="14" name="dateModified"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="120" parent="14" name="isSynced"> | ||||
|     <column id="121" parent="14" name="isSynced"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="121" parent="14" name="hash"> | ||||
|     <column id="122" parent="14" name="hash"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="122" parent="14" name="dateCreated"> | ||||
|     <column id="123" parent="14" name="dateCreated"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="123" parent="14" name="sqlite_autoindex_options_1"> | ||||
|     <index id="124" parent="14" name="sqlite_autoindex_options_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>name</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <key id="124" parent="14"> | ||||
|     <key id="125" parent="14"> | ||||
|       <ColNames>name</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="125" parent="15" name="branchId"> | ||||
|     <column id="126" parent="15" name="branchId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="126" parent="15" name="notePath"> | ||||
|     <column id="127" parent="15" name="notePath"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="127" parent="15" name="hash"> | ||||
|     <column id="128" parent="15" name="hash"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="128" parent="15" name="dateCreated"> | ||||
|     <column id="129" parent="15" name="dateCreated"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="129" parent="15" name="isDeleted"> | ||||
|     <column id="130" parent="15" name="isDeleted"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|     </column> | ||||
|     <index id="130" parent="15" name="sqlite_autoindex_recent_notes_1"> | ||||
|     <index id="131" parent="15" name="sqlite_autoindex_recent_notes_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>branchId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <key id="131" parent="15"> | ||||
|     <key id="132" parent="15"> | ||||
|       <ColNames>branchId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="132" parent="16" name="sourceId"> | ||||
|     <column id="133" parent="16" name="sourceId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="133" parent="16" name="dateCreated"> | ||||
|     <column id="134" parent="16" name="dateCreated"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="134" parent="16" name="sqlite_autoindex_source_ids_1"> | ||||
|     <index id="135" parent="16" name="sqlite_autoindex_source_ids_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>sourceId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <key id="135" parent="16"> | ||||
|     <key id="136" parent="16"> | ||||
|       <ColNames>sourceId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="136" parent="17" name="type"> | ||||
|     <column id="137" parent="17" name="type"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="137" parent="17" name="name"> | ||||
|     <column id="138" parent="17" name="name"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="138" parent="17" name="tbl_name"> | ||||
|     <column id="139" parent="17" name="tbl_name"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="139" parent="17" name="rootpage"> | ||||
|     <column id="140" parent="17" name="rootpage"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>integer|0s</DataType> | ||||
|     </column> | ||||
|     <column id="140" parent="17" name="sql"> | ||||
|     <column id="141" parent="17" name="sql"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="141" parent="18" name="name"> | ||||
|     <column id="142" parent="18" name="name"> | ||||
|       <Position>1</Position> | ||||
|     </column> | ||||
|     <column id="142" parent="18" name="seq"> | ||||
|     <column id="143" parent="18" name="seq"> | ||||
|       <Position>2</Position> | ||||
|     </column> | ||||
|     <column id="143" parent="19" name="id"> | ||||
|     <column id="144" parent="19" name="id"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <SequenceIdentity>1</SequenceIdentity> | ||||
|     </column> | ||||
|     <column id="144" parent="19" name="entityName"> | ||||
|     <column id="145" parent="19" name="entityName"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="145" parent="19" name="entityId"> | ||||
|     <column id="146" parent="19" name="entityId"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="146" parent="19" name="sourceId"> | ||||
|     <column id="147" parent="19" name="sourceId"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="147" parent="19" name="syncDate"> | ||||
|     <column id="148" parent="19" name="syncDate"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="148" parent="19" name="IDX_sync_entityName_entityId"> | ||||
|     <index id="149" parent="19" name="IDX_sync_entityName_entityId"> | ||||
|       <ColNames>entityName | ||||
| entityId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="149" parent="19" name="IDX_sync_syncDate"> | ||||
|     <index id="150" parent="19" name="IDX_sync_syncDate"> | ||||
|       <ColNames>syncDate</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="150" parent="19"> | ||||
|     <key id="151" parent="19"> | ||||
|       <ColNames>id</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|     </key> | ||||
|   | ||||
| @@ -3,6 +3,7 @@ CREATE TABLE IF NOT EXISTS "note_contents" ( | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `content`	TEXT NULL DEFAULT NULL, | ||||
|   `hash` TEXT DEFAULT "" NOT NULL, | ||||
|   PRIMARY KEY(`noteContentId`) | ||||
| ); | ||||
|  | ||||
|   | ||||
| @@ -67,6 +67,13 @@ class Note extends Entity { | ||||
|         return this.noteContent; | ||||
|     } | ||||
|  | ||||
|     /** @returns {Promise<*>} */ | ||||
|     async getContent() { | ||||
|         const noteContent = await this.getNoteContent(); | ||||
|  | ||||
|         return noteContent.content; | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if this note is the root of the note tree. Root note has "root" noteId */ | ||||
|     isRoot() { | ||||
|         return this.noteId === 'root'; | ||||
| @@ -606,10 +613,6 @@ class Note extends Entity { | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         if (this.isJson() && this.jsonContent) { | ||||
|             this.content = JSON.stringify(this.jsonContent, null, '\t'); | ||||
|         } | ||||
|  | ||||
|         // we do this here because encryption needs the note ID for the IV | ||||
|         this.generateIdIfNecessary(); | ||||
|  | ||||
| @@ -637,7 +640,6 @@ class Note extends Entity { | ||||
|             else { | ||||
|                 // updating protected note outside of protected session means we will keep original ciphertexts | ||||
|                 pojo.title = pojo.titleCipherText; | ||||
|                 pojo.content = pojo.contentCipherText; | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -645,7 +647,6 @@ class Note extends Entity { | ||||
|         delete pojo.isContentAvailable; | ||||
|         delete pojo.__attributeCache; | ||||
|         delete pojo.titleCipherText; | ||||
|         delete pojo.contentCipherText; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ function showDialog() { | ||||
|  | ||||
|     $dialog.modal(); | ||||
|  | ||||
|     const noteText = noteDetailService.getCurrentNote().content; | ||||
|     const noteText = noteDetailService.getCurrentNote().noteContent.content; | ||||
|  | ||||
|     $noteSource.text(formatHtml(noteText)); | ||||
| } | ||||
|   | ||||
| @@ -117,7 +117,7 @@ async function saveNote() { | ||||
|     } | ||||
|  | ||||
|     note.title = $noteTitle.val(); | ||||
|     note.content = getCurrentNoteContent(note); | ||||
|     note.noteContent.content = getCurrentNoteContent(note); | ||||
|  | ||||
|     // it's important to set the flag back to false immediatelly after retrieving title and content | ||||
|     // otherwise we might overwrite another change (especially async code) | ||||
|   | ||||
| @@ -111,13 +111,13 @@ async function renderTooltip(note, attributes) { | ||||
|     } | ||||
|  | ||||
|     if (note.type === 'text') { | ||||
|         // surround with <div> for a case when note.content is pure text (e.g. "[protected]") which | ||||
|         // surround with <div> for a case when note's content is pure text (e.g. "[protected]") which | ||||
|         // then fails the jquery non-empty text test | ||||
|         content += '<div>' + note.content + '</div>'; | ||||
|         content += '<div>' + note.noteContent.content + '</div>'; | ||||
|     } | ||||
|     else if (note.type === 'code') { | ||||
|         content += $("<pre>") | ||||
|             .text(note.content) | ||||
|             .text(note.noteContent.content) | ||||
|             .prop('outerHTML'); | ||||
|     } | ||||
|     else if (note.type === 'image') { | ||||
|   | ||||
| @@ -51,7 +51,7 @@ async function downloadNoteFile(noteId, res) { | ||||
|     res.setHeader('Content-Disposition', utils.getContentDisposition(fileName)); | ||||
|     res.setHeader('Content-Type', note.mime); | ||||
|  | ||||
|     res.send(note.content); | ||||
|     res.send((await note.getNoteContent()).content); | ||||
| } | ||||
|  | ||||
| async function downloadFile(req, res) { | ||||
|   | ||||
| @@ -21,7 +21,7 @@ async function returnImage(req, res) { | ||||
|  | ||||
|     res.set('Content-Type', image.mime); | ||||
|  | ||||
|     res.send(image.content); | ||||
|     res.send((await note.getNoteContent()).content); | ||||
| } | ||||
|  | ||||
| async function uploadImage(req) { | ||||
|   | ||||
| @@ -17,7 +17,7 @@ async function exportToOpml(branch, res) { | ||||
|         const title = (branch.prefix ? (branch.prefix + ' - ') : '') + note.title; | ||||
|  | ||||
|         const preparedTitle = prepareText(title); | ||||
|         const preparedContent = prepareText(note.content); | ||||
|         const preparedContent = prepareText(await note.getContent()); | ||||
|  | ||||
|         res.write(`<outline title="${preparedTitle}" text="${preparedContent}">\n`); | ||||
|  | ||||
|   | ||||
| @@ -18,30 +18,32 @@ async function exportSingleNote(branch, format, res) { | ||||
|  | ||||
|     let payload, extension, mime; | ||||
|  | ||||
|     const noteContent = await note.getNoteContent(); | ||||
|  | ||||
|     if (note.type === 'text') { | ||||
|         if (format === 'html') { | ||||
|             if (!note.content.toLowerCase().includes("<html")) { | ||||
|                 note.content = '<html><head><meta charset="utf-8"></head><body>' + note.content + '</body></html>'; | ||||
|             if (!noteContent.content.toLowerCase().includes("<html")) { | ||||
|                 noteContent.content = '<html><head><meta charset="utf-8"></head><body>' + noteContent.content + '</body></html>'; | ||||
|             } | ||||
|  | ||||
|             payload = html.prettyPrint(note.content, {indent_size: 2}); | ||||
|             payload = html.prettyPrint(noteContent.content, {indent_size: 2}); | ||||
|             extension = 'html'; | ||||
|             mime = 'text/html'; | ||||
|         } | ||||
|         else if (format === 'markdown') { | ||||
|             const turndownService = new TurndownService(); | ||||
|             payload = turndownService.turndown(note.content); | ||||
|             payload = turndownService.turndown(noteContent.content); | ||||
|             extension = 'md'; | ||||
|             mime = 'text/markdown' | ||||
|         } | ||||
|     } | ||||
|     else if (note.type === 'code') { | ||||
|         payload = note.content; | ||||
|         payload = noteContent.content; | ||||
|         extension = mimeTypes.extension(note.mime) || 'code'; | ||||
|         mime = note.mime; | ||||
|     } | ||||
|     else if (note.type === 'relation-map' || note.type === 'search') { | ||||
|         payload = note.content; | ||||
|         payload = noteContent.content; | ||||
|         extension = 'json'; | ||||
|         mime = 'application/json'; | ||||
|     } | ||||
|   | ||||
| @@ -123,7 +123,7 @@ async function exportToTar(branch, format, res) { | ||||
|         const childBranches = await note.getChildBranches(); | ||||
|  | ||||
|         // if it's a leaf then we'll export it even if it's empty | ||||
|         if (note.content.length > 0 || childBranches.length === 0) { | ||||
|         if ((await note.getContent()).length > 0 || childBranches.length === 0) { | ||||
|             meta.dataFileName = getDataFileName(note, baseFileName, existingFileNames); | ||||
|         } | ||||
|  | ||||
| @@ -147,19 +147,21 @@ async function exportToTar(branch, format, res) { | ||||
|         return meta; | ||||
|     } | ||||
|  | ||||
|     function prepareContent(note, format) { | ||||
|     async function prepareContent(note, format) { | ||||
|         const content = await note.getContent(); | ||||
|  | ||||
|         if (format === 'html') { | ||||
|             if (!note.content.toLowerCase().includes("<html")) { | ||||
|                 note.content = '<html><head><meta charset="utf-8"></head><body>' + note.content + '</body></html>'; | ||||
|             if (!content.toLowerCase().includes("<html")) { | ||||
|                 note.content = '<html><head><meta charset="utf-8"></head><body>' + content + '</body></html>'; | ||||
|             } | ||||
|  | ||||
|             return html.prettyPrint(note.content, {indent_size: 2}); | ||||
|             return html.prettyPrint(content, {indent_size: 2}); | ||||
|         } | ||||
|         else if (format === 'markdown') { | ||||
|             return turndownService.turndown(note.content); | ||||
|             return turndownService.turndown(content); | ||||
|         } | ||||
|         else { | ||||
|             return note.content; | ||||
|             return content; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -179,7 +181,7 @@ async function exportToTar(branch, format, res) { | ||||
|         notePaths[note.noteId] = path + (noteMeta.dataFileName || noteMeta.dirFileName); | ||||
|  | ||||
|         if (noteMeta.dataFileName) { | ||||
|             const content = prepareContent(note, noteMeta.format); | ||||
|             const content = await prepareContent(note, noteMeta.format); | ||||
|  | ||||
|             pack.entry({name: path + noteMeta.dataFileName, size: content.length}, content); | ||||
|         } | ||||
|   | ||||
| @@ -218,6 +218,8 @@ async function importEnex(file, parentNote) { | ||||
|             mime: 'text/html' | ||||
|         })).note; | ||||
|  | ||||
|         const noteContent = await noteEntity.getNoteContent(); | ||||
|  | ||||
|         for (const resource of resources) { | ||||
|             const hash = utils.md5(resource.content); | ||||
|  | ||||
| @@ -238,8 +240,8 @@ async function importEnex(file, parentNote) { | ||||
|  | ||||
|               const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`; | ||||
|  | ||||
|               noteEntity.content = noteEntity.content.replace(mediaRegex, resourceLink); | ||||
|             } | ||||
|               noteContent.content = noteContent.content.replace(mediaRegex, resourceLink); | ||||
|             }; | ||||
|  | ||||
|             if (["image/jpeg", "image/png", "image/gif"].includes(resource.mime)) { | ||||
|               try { | ||||
| @@ -249,12 +251,12 @@ async function importEnex(file, parentNote) { | ||||
|  | ||||
|                 const imageLink = `<img src="${url}">`; | ||||
|  | ||||
|                 noteEntity.content = noteEntity.content.replace(mediaRegex, imageLink); | ||||
|                 noteContent.content = noteContent.content.replace(mediaRegex, imageLink); | ||||
|  | ||||
|                 if (!note.content.includes(imageLink)) { | ||||
|                 if (!noteContent.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 | ||||
|                     note.content += imageLink; | ||||
|                     noteContent.content += imageLink; | ||||
|                 } | ||||
|               } catch (e) { | ||||
|                 log.error("error when saving image from ENEX file: " + e); | ||||
| @@ -267,7 +269,7 @@ async function importEnex(file, parentNote) { | ||||
|         } | ||||
|  | ||||
|         // save updated content with links to files/images | ||||
|         await noteEntity.save(); | ||||
|         await noteContent.save(); | ||||
|     } | ||||
|  | ||||
|     saxStream.on("closetag", async tag => { | ||||
|   | ||||
| @@ -245,8 +245,10 @@ async function importTar(fileBuffer, importRootNote) { | ||||
|         let note = await repository.getNote(noteId); | ||||
|  | ||||
|         if (note) { | ||||
|             note.content = content; | ||||
|             await note.save(); | ||||
|             const noteContent = await note.getNoteContent(); | ||||
|  | ||||
|             noteContent.content = content; | ||||
|             await noteContent.save(); | ||||
|         } | ||||
|         else { | ||||
|             const noteTitle = getNoteTitle(filePath, noteMeta); | ||||
|   | ||||
| @@ -8,6 +8,7 @@ const eventService = require('./events'); | ||||
| const repository = require('./repository'); | ||||
| const cls = require('../services/cls'); | ||||
| const Note = require('../entities/note'); | ||||
| const NoteContent = require('../entities/note_content'); | ||||
| const Link = require('../entities/link'); | ||||
| const NoteRevision = require('../entities/note_revision'); | ||||
| const Branch = require('../entities/branch'); | ||||
| @@ -87,12 +88,16 @@ async function createNewNote(parentNoteId, noteData) { | ||||
|     const note = await new Note({ | ||||
|         noteId: noteData.noteId, // optionally can force specific noteId | ||||
|         title: noteData.title, | ||||
|         content: noteData.content, | ||||
|         isProtected: noteData.isProtected, | ||||
|         type: noteData.type || 'text', | ||||
|         mime: noteData.mime || 'text/html' | ||||
|     }).save(); | ||||
|  | ||||
|     note.noteContent = await new NoteContent({ | ||||
|         noteId: note.noteId, | ||||
|         content: noteData.content | ||||
|     }); | ||||
|  | ||||
|     const branch = await new Branch({ | ||||
|         noteId: note.noteId, | ||||
|         parentNoteId: parentNoteId, | ||||
| @@ -284,6 +289,12 @@ async function saveLinks(note, content) { | ||||
| } | ||||
|  | ||||
| async function saveNoteRevision(note) { | ||||
|     // files and images are immutable, they can't be updated | ||||
|     // but we don't even version titles which is probably not correct | ||||
|     if (note.type !== 'file' || note.type !== 'image' || await note.hasLabel('disableVersioning')) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const now = new Date(); | ||||
|     const noteRevisionSnapshotTimeInterval = parseInt(await optionService.getOption('noteRevisionSnapshotTimeInterval')); | ||||
|  | ||||
| @@ -294,16 +305,12 @@ async function saveNoteRevision(note) { | ||||
|  | ||||
|     const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.dateCreated).getTime(); | ||||
|  | ||||
|     if (note.type !== 'file' | ||||
|         && !await note.hasLabel('disableVersioning') | ||||
|         && !existingNoteRevisionId | ||||
|         && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { | ||||
|  | ||||
|     if (!existingNoteRevisionId && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { | ||||
|         await new NoteRevision({ | ||||
|             noteId: note.noteId, | ||||
|             // title and text should be decrypted now | ||||
|             title: note.title, | ||||
|             content: note.content, | ||||
|             content: note.noteContent.content, | ||||
|             type: note.type, | ||||
|             mime: note.mime, | ||||
|             isProtected: false, // will be fixed in the protectNoteRevisions() call | ||||
| @@ -320,22 +327,23 @@ async function updateNote(noteId, noteUpdates) { | ||||
|         throw new Error(`Note ${noteId} is not available for change!`); | ||||
|     } | ||||
|  | ||||
|     if (note.type === 'file' || note.type === 'image') { | ||||
|         // files and images are immutable, they can't be updated | ||||
|         noteUpdates.content = note.content; | ||||
|     } | ||||
|  | ||||
|     await saveNoteRevision(note); | ||||
|  | ||||
|     const noteTitleChanged = note.title !== noteUpdates.title; | ||||
|  | ||||
|     noteUpdates.content = await saveLinks(note, noteUpdates.content); | ||||
|     noteUpdates.noteContent.content = await saveLinks(note, noteUpdates.noteContent.content); | ||||
|  | ||||
|     note.title = noteUpdates.title; | ||||
|     note.setContent(noteUpdates.content); | ||||
|     note.isProtected = noteUpdates.isProtected; | ||||
|     await note.save(); | ||||
|  | ||||
|     if (note.type !== 'file' && note.type !== 'image') { | ||||
|         const noteContent = await note.getNoteContent(); | ||||
|         noteContent.content = noteUpdates.noteContent.content; | ||||
|         noteContent.isProtected = noteUpdates.isProtected; | ||||
|         await noteContent.save(); | ||||
|     } | ||||
|  | ||||
|     if (noteTitleChanged) { | ||||
|         await triggerNoteTitleChanged(note); | ||||
|     } | ||||
| @@ -394,7 +402,7 @@ async function cleanupDeletedNotes() { | ||||
|     // it's better to not use repository for this because it will complain about saving protected notes | ||||
|     // out of protected session | ||||
|  | ||||
|     await sql.execute("UPDATE notes SET content = NULL WHERE isDeleted = 1 AND content IS NOT NULL AND dateModified <= ?", [dateUtils.dateStr(cutoffDate)]); | ||||
|     await sql.execute("UPDATE note_contents SET content = NULL WHERE content IS NOT NULL AND noteId IN (SELECT noteId FROM notes WHERE isDeleted = 1 AND notes.dateModified <= ?)", [dateUtils.dateStr(cutoffDate)]); | ||||
|  | ||||
|     await sql.execute("UPDATE note_revisions SET content = NULL WHERE note_revisions.content IS NOT NULL AND noteId IN (SELECT noteId FROM notes WHERE isDeleted = 1 AND notes.dateModified <= ?)", [dateUtils.dateStr(cutoffDate)]); | ||||
| } | ||||
|   | ||||
| @@ -42,6 +42,14 @@ async function getNote(noteId) { | ||||
|     return await getEntity("SELECT * FROM notes WHERE noteId = ?", [noteId]); | ||||
| } | ||||
|  | ||||
| /** @returns {Promise<Note|null>} */ | ||||
| async function getNoteWithContent(noteId) { | ||||
|     const note = await getEntity("SELECT * FROM notes WHERE noteId = ?", [noteId]); | ||||
|     await note.getNoteContent(); | ||||
|  | ||||
|     return note; | ||||
| } | ||||
|  | ||||
| /** @returns {Promise<NoteContent|null>} */ | ||||
| async function getNoteContent(noteContentId) { | ||||
|     return await getEntity("SELECT * FROM note_contents WHERE noteContentId = ?", [noteContentId]); | ||||
| @@ -126,6 +134,7 @@ module.exports = { | ||||
|     getEntities, | ||||
|     getEntity, | ||||
|     getNote, | ||||
|     getNoteWithContent, | ||||
|     getNoteContent, | ||||
|     getBranch, | ||||
|     getAttribute, | ||||
|   | ||||
| @@ -56,10 +56,10 @@ async function executeBundle(bundle, apiParams = {}) { | ||||
|  */ | ||||
| async function executeScript(script, params, startNoteId, currentNoteId, originEntityName, originEntityId) { | ||||
|     const startNote = await repository.getNote(startNoteId); | ||||
|     const currentNote = await repository.getNote(currentNoteId); | ||||
|     const currentNote = await repository.getNoteWithContent(currentNoteId); | ||||
|     const originEntity = await repository.getEntityFromName(originEntityName, originEntityId); | ||||
|  | ||||
|     currentNote.content = `return await (${script}\r\n)(${getParams(params)})`; | ||||
|     currentNote.noteContent.content = `return await (${script}\r\n)(${getParams(params)})`; | ||||
|     currentNote.type = 'code'; | ||||
|     currentNote.mime = 'application/javascript;env=backend'; | ||||
|  | ||||
| @@ -158,7 +158,7 @@ apiContext.modules['${note.noteId}'] = {}; | ||||
| ${root ? 'return ' : ''}await ((async function(exports, module, require, api` + (modules.length > 0 ? ', ' : '') + | ||||
|             modules.map(child => sanitizeVariableName(child.title)).join(', ') + `) { | ||||
| try { | ||||
| ${note.content}; | ||||
| ${await note.getContent()}; | ||||
| } catch (e) { throw new Error("Load of script note \\"${note.title}\\" (${note.noteId}) failed with: " + e.message); } | ||||
| if (!module.exports) module.exports = {}; | ||||
| for (const exportKey in exports) module.exports[exportKey] = exports[exportKey]; | ||||
| @@ -167,7 +167,7 @@ for (const exportKey in exports) module.exports[exportKey] = exports[exportKey]; | ||||
| `; | ||||
|     } | ||||
|     else if (note.isHtml()) { | ||||
|         bundle.html += note.content; | ||||
|         bundle.html += await note.getContent(); | ||||
|     } | ||||
|  | ||||
|     return bundle; | ||||
|   | ||||
| @@ -48,14 +48,16 @@ async function updateEntity(sync, entity, sourceId) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function deserializeNoteContentBuffer(note) { | ||||
|     if (note.content !== null && (note.type === 'file' || note.type === 'image')) { | ||||
|         note.content = Buffer.from(note.content, 'base64'); | ||||
| async function deserializeNoteContentBuffer(note) { | ||||
|     const noteContent = await note.getNoteContent(); | ||||
|  | ||||
|     if (noteContent.content !== null && (note.type === 'file' || note.type === 'image')) { | ||||
|         noteContent.content = Buffer.from(noteContent.content, 'base64'); | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function updateNote(entity, sourceId) { | ||||
|     deserializeNoteContentBuffer(entity); | ||||
|     await deserializeNoteContentBuffer(entity); | ||||
|  | ||||
|     const origNote = await sql.getRow("SELECT * FROM notes WHERE noteId = ?", [entity.noteId]); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user