mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	Compare commits
	
		
			10 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 535dcb6d12 | ||
|  | 4426362799 | ||
|  | 2c609e8136 | ||
|  | 11b73b79ed | ||
|  | e70c862e72 | ||
|  | b3e66d5a83 | ||
|  | e8cd821e57 | ||
|  | be7ac74235 | ||
|  | 58fa0832f6 | ||
|  | 1502b9ce66 | 
| @@ -33,6 +33,9 @@ find $DIR/libraries -name "*.map" -type f -delete | ||||
|  | ||||
| rm -r $DIR/src/public/app | ||||
|  | ||||
| rm -r $DIR/node_modules/sqlite3/build | ||||
| rm -r $DIR/node_modules/sqlite3/deps | ||||
|  | ||||
| sed -i -e 's/app\/desktop.js/app-dist\/desktop.js/g' $DIR/src/views/desktop.ejs | ||||
| sed -i -e 's/app\/mobile.js/app-dist\/mobile.js/g' $DIR/src/views/mobile.ejs | ||||
| sed -i -e 's/app\/setup.js/app-dist\/setup.js/g' $DIR/src/views/setup.ejs | ||||
| sed -i -e 's/app\/setup.js/app-dist\/setup.js/g' $DIR/src/views/setup.ejs | ||||
|   | ||||
							
								
								
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "trilium", | ||||
|   "version": "0.42.5", | ||||
|   "version": "0.42.6", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "name": "trilium", | ||||
|   "productName": "Trilium Notes", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.42.6", | ||||
|   "version": "0.42.7", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "main": "electron.js", | ||||
|   "bin": { | ||||
|   | ||||
| @@ -1,12 +1,24 @@ | ||||
| const backupService = require('./services/backup'); | ||||
| const sqlInit = require('./services/sql_init'); | ||||
| require('./entities/entity_constructor'); | ||||
|  | ||||
| backupService.anonymize().then(resp => { | ||||
|     if (resp.success) { | ||||
|         console.log("Anonymization failed."); | ||||
| sqlInit.dbReady.then(async () => { | ||||
|     try { | ||||
|         console.log("Starting anonymization..."); | ||||
|  | ||||
|         const resp = await backupService.anonymize(); | ||||
|  | ||||
|         if (resp.success) { | ||||
|             console.log("Anonymized file has been saved to: " + resp.anonymizedFilePath); | ||||
|  | ||||
|             process.exit(0); | ||||
|         } else { | ||||
|             console.log("Anonymization failed."); | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
|         console.log("Anonymized file has been saved to: " + resp.anonymizedFilePath); | ||||
|     catch (e) { | ||||
|         console.error(e.message, e.stack); | ||||
|     } | ||||
|  | ||||
|     process.exit(0); | ||||
|     process.exit(1); | ||||
| }); | ||||
|   | ||||
| @@ -105,7 +105,7 @@ class Attribute extends Entity { | ||||
|  | ||||
|     // cannot be static! | ||||
|     updatePojo(pojo) { | ||||
|         delete pojo.__note; | ||||
|         delete pojo.__note; // FIXME: probably note necessary anymore | ||||
|     } | ||||
|  | ||||
|     createClone(type, name, value) { | ||||
|   | ||||
| @@ -152,10 +152,10 @@ function AttributesModel() { | ||||
|                 attr.value = treeService.getNoteIdFromNotePath(attr.selectedPath); | ||||
|             } | ||||
|             else if (attr.type === 'label-definition') { | ||||
|                 attr.value = attr.labelDefinition; | ||||
|                 attr.value = JSON.stringify(attr.labelDefinition); | ||||
|             } | ||||
|             else if (attr.type === 'relation-definition') { | ||||
|                 attr.value = attr.relationDefinition; | ||||
|                 attr.value = JSON.stringify(attr.relationDefinition); | ||||
|             } | ||||
|  | ||||
|             delete attr.labelValue; | ||||
|   | ||||
| @@ -170,6 +170,16 @@ class NoteShort { | ||||
|      * @returns {Attribute[]} all note's attributes, including inherited ones | ||||
|      */ | ||||
|     getAttributes(type, name) { | ||||
|         return this.__filterAttrs(this.__getCachedAttributes([]), type, name); | ||||
|     } | ||||
|  | ||||
|     __getCachedAttributes(path) { | ||||
|         // notes/clones cannot form tree cycles, it is possible to create attribute inheritance cycle via templates | ||||
|         // when template instance is a parent of template itself | ||||
|         if (path.includes(this.noteId)) { | ||||
|             return []; | ||||
|         } | ||||
|  | ||||
|         if (!(this.noteId in noteAttributeCache)) { | ||||
|             const ownedAttributes = this.getOwnedAttributes(); | ||||
|  | ||||
| @@ -177,11 +187,13 @@ class NoteShort { | ||||
|                 ownedAttributes | ||||
|             ]; | ||||
|  | ||||
|             const newPath = [...path, this.noteId]; | ||||
|  | ||||
|             for (const templateAttr of ownedAttributes.filter(oa => oa.type === 'relation' && oa.name === 'template')) { | ||||
|                 const templateNote = this.treeCache.notes[templateAttr.value]; | ||||
|  | ||||
|                 if (templateNote) { | ||||
|                     attrArrs.push(templateNote.getAttributes()); | ||||
|                 if (templateNote && templateNote.noteId !== this.noteId) { | ||||
|                     attrArrs.push(templateNote.__getCachedAttributes(newPath)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -189,7 +201,7 @@ class NoteShort { | ||||
|                 for (const parentNote of this.getParentNotes()) { | ||||
|                     // these virtual parent-child relationships are also loaded into frontend tree cache | ||||
|                     if (parentNote.type !== 'search') { | ||||
|                         attrArrs.push(parentNote.getInheritableAttributes()); | ||||
|                         attrArrs.push(parentNote.__getInheritableAttributes(newPath)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -197,7 +209,7 @@ class NoteShort { | ||||
|             noteAttributeCache.attributes[this.noteId] = attrArrs.flat(); | ||||
|         } | ||||
|  | ||||
|         return this.__filterAttrs(noteAttributeCache.attributes[this.noteId], type, name); | ||||
|         return noteAttributeCache.attributes[this.noteId]; | ||||
|     } | ||||
|  | ||||
|     __filterAttrs(attributes, type, name) { | ||||
| @@ -212,8 +224,8 @@ class NoteShort { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     getInheritableAttributes() { | ||||
|         const attrs = this.getAttributes(); | ||||
|     __getInheritableAttributes(path) { | ||||
|         const attrs = this.__getCachedAttributes(path); | ||||
|  | ||||
|         return attrs.filter(attr => attr.isInheritable); | ||||
|     } | ||||
| @@ -460,4 +472,4 @@ class NoteShort { | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default NoteShort; | ||||
| export default NoteShort; | ||||
|   | ||||
| @@ -54,8 +54,9 @@ export default class LoadResults { | ||||
|         this.attributes.push({attributeId, sourceId}); | ||||
|     } | ||||
|  | ||||
|     getAttributes() { | ||||
|     getAttributes(sourceId = 'none') { | ||||
|         return this.attributes | ||||
|             .filter(row => row.sourceId !== sourceId) | ||||
|             .map(row => this.treeCache.attributes[row.attributeId]) | ||||
|             .filter(attr => !!attr); | ||||
|     } | ||||
|   | ||||
| @@ -34,7 +34,7 @@ export default class MainTreeExecutors extends Component { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const {note} = await noteCreateService.createNote(activeNote.noteId, { | ||||
|         await noteCreateService.createNote(activeNote.noteId, { | ||||
|             isProtected: activeNote.isProtected, | ||||
|             saveSelection: false | ||||
|         }); | ||||
| @@ -56,4 +56,4 @@ export default class MainTreeExecutors extends Component { | ||||
|             saveSelection: false | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -73,7 +73,7 @@ class TabContext extends Component { | ||||
|         protectedSessionHolder.touchProtectedSessionIfNecessary(this.note); | ||||
|  | ||||
|         if (triggerSwitchEvent) { | ||||
|             this.triggerEvent('tabNoteSwitched', { | ||||
|             await this.triggerEvent('tabNoteSwitched', { | ||||
|                 tabContext: this, | ||||
|                 notePath: this.notePath | ||||
|             }); | ||||
| @@ -127,4 +127,4 @@ class TabContext extends Component { | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default TabContext; | ||||
| export default TabContext; | ||||
|   | ||||
| @@ -203,7 +203,7 @@ export default class TabManager extends Component { | ||||
|         if (activate) { | ||||
|             this.activateTab(tabContext.tabId, false); | ||||
|  | ||||
|             this.triggerEvent('tabNoteSwitchedAndActivated', { | ||||
|             await this.triggerEvent('tabNoteSwitchedAndActivated', { | ||||
|                 tabContext, | ||||
|                 notePath: tabContext.notePath // resolved note path | ||||
|             }); | ||||
|   | ||||
| @@ -4,7 +4,6 @@ const TPL = ` | ||||
| <table class="note-info-widget-table"> | ||||
|     <style> | ||||
|         .note-info-widget-table { | ||||
|             table-layout: fixed;  | ||||
|             width: 100%; | ||||
|         }  | ||||
|     | ||||
| @@ -22,22 +21,23 @@ const TPL = ` | ||||
|  | ||||
|     <tr> | ||||
|         <th>Note ID:</th> | ||||
|         <td colspan="3" class="note-info-note-id"></td> | ||||
|         <td class="note-info-note-id"></td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|         <th>Created:</th> | ||||
|         <td colspan="3" class="note-info-date-created"></td> | ||||
|         <td class="note-info-date-created"></td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|         <th>Modified:</th> | ||||
|         <td colspan="3" class="note-info-date-modified"></td> | ||||
|         <td class="note-info-date-modified"></td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|         <th>Type:</th> | ||||
|         <td class="note-info-type"></td> | ||||
|          | ||||
|         <th>MIME:</th> | ||||
|         <td class="note-info-mime"></td> | ||||
|         <td> | ||||
|             <span class="note-info-type"></span> | ||||
|              | ||||
|             <span class="note-info-mime"></span> | ||||
|         </td> | ||||
|     </tr> | ||||
| </table> | ||||
| `; | ||||
| @@ -69,9 +69,12 @@ export default class NoteInfoWidget extends CollapsibleWidget { | ||||
|  | ||||
|         this.$type.text(note.type); | ||||
|  | ||||
|         this.$mime | ||||
|             .text(note.mime) | ||||
|             .attr("title", note.mime); | ||||
|         if (note.mime) { | ||||
|             this.$mime.text('(' + note.mime + ')'); | ||||
|         } | ||||
|         else { | ||||
|             this.$mime.empty(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     entitiesReloadedEvent({loadResults}) { | ||||
|   | ||||
| @@ -854,8 +854,11 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|         this.toggleInt(this.isEnabled()); | ||||
|  | ||||
|         const oldActiveNode = this.getActiveNode(); | ||||
|         let oldActiveNodeFocused = false; | ||||
|  | ||||
|         if (oldActiveNode) { | ||||
|             oldActiveNodeFocused = oldActiveNode.hasFocus(); | ||||
|  | ||||
|             oldActiveNode.setActive(false); | ||||
|             oldActiveNode.setFocus(false); | ||||
|         } | ||||
| @@ -868,8 +871,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|                     await this.expandToNote(this.tabContext.notePath); | ||||
|                 } | ||||
|  | ||||
|                 newActiveNode.setActive(true, {noEvents: true}); | ||||
|  | ||||
|                 newActiveNode.setActive(true, {noEvents: true, noFocus: !oldActiveNodeFocused}); | ||||
|                 newActiveNode.makeVisible({scrollIntoView: true}); | ||||
|             } | ||||
|         } | ||||
| @@ -898,7 +900,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|  | ||||
|     async entitiesReloadedEvent({loadResults}) { | ||||
|         const activeNode = this.getActiveNode(); | ||||
|         const activeNodeFocused = activeNode ? activeNode.hasFocus() : false; | ||||
|         const activeNodeFocused = activeNode && activeNode.hasFocus(); | ||||
|         const nextNode = activeNode ? (activeNode.getNextSibling() || activeNode.getPrevSibling() || activeNode.getParent()) : null; | ||||
|         const activeNotePath = activeNode ? treeService.getNotePath(activeNode) : null; | ||||
|         const nextNotePath = nextNode ? treeService.getNotePath(nextNode) : null; | ||||
| @@ -1021,7 +1023,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|             } | ||||
|  | ||||
|             if (node) { | ||||
|                 node.setActive(true, {noEvents: true}); | ||||
|                 node.setActive(true, {noEvents: true, noFocus: true}); | ||||
|             } | ||||
|             else { | ||||
|                 // this is used when original note has been deleted and we want to move the focus to the note above/below | ||||
| @@ -1036,7 +1038,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|  | ||||
|             // return focus if the previously active node was also focused | ||||
|             if (newActiveNode && activeNodeFocused) { | ||||
|                 newActiveNode.setFocus(true); | ||||
|                 await newActiveNode.setFocus(true); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -1064,7 +1066,7 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|         if (activeNotePath) { | ||||
|             const node = await this.getNodeFromPath(activeNotePath, true); | ||||
|  | ||||
|             await node.setActive(true, {noEvents: true}); | ||||
|             await node.setActive(true, {noEvents: true, noFocus: true}); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -229,7 +229,7 @@ export default class PromotedAttributesWidget extends TabAwareWidget { | ||||
|                 .prop("title", "Remove this attribute") | ||||
|                 .on('click', async () => { | ||||
|                     if (valueAttr.attributeId) { | ||||
|                         await server.remove("notes/" + this.noteId + "/attributes/" + valueAttr.attributeId); | ||||
|                         await server.remove("notes/" + this.noteId + "/attributes/" + valueAttr.attributeId, this.componentId); | ||||
|                     } | ||||
|  | ||||
|                     $tr.remove(); | ||||
| @@ -263,8 +263,19 @@ export default class PromotedAttributesWidget extends TabAwareWidget { | ||||
|             type: $attr.prop("attribute-type"), | ||||
|             name: $attr.prop("attribute-name"), | ||||
|             value: value | ||||
|         }); | ||||
|         }, this.componentId); | ||||
|  | ||||
|         $attr.prop("attribute-id", result.attributeId); | ||||
|     } | ||||
|  | ||||
|     entitiesReloadedEvent({loadResults}) {console.log("loadResults", loadResults); | ||||
|         // relation/label definitions are very often inherited by tree or template, | ||||
|         // it's difficult to detect inheritance so we will | ||||
|         if (loadResults.getAttributes(this.componentId).find(attr => | ||||
|             attr.noteId === this.noteId | ||||
|             || ['label-definition', 'relation-definition'].includes(attr.type))) { | ||||
|  | ||||
|             this.refresh(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -115,13 +115,24 @@ function isAttributeType(type) { | ||||
| } | ||||
|  | ||||
| function isAttributeDangerous(type, name) { | ||||
|     return BUILTIN_ATTRIBUTES.some(attr =>  | ||||
|         attr.type === attr.type &&  | ||||
|     return BUILTIN_ATTRIBUTES.some(attr => | ||||
|         attr.type === attr.type && | ||||
|         attr.name.toLowerCase() === name.trim().toLowerCase() && | ||||
|         attr.isDangerous | ||||
|     ); | ||||
| } | ||||
|  | ||||
| function getBuiltinAttributeNames() { | ||||
|     return BUILTIN_ATTRIBUTES | ||||
|         .map(attr => attr.name) | ||||
|         .concat([ | ||||
|             'internalLink', | ||||
|             'imageLink', | ||||
|             'includeNoteLink', | ||||
|             'relationMapLink' | ||||
|         ]); | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     getNotesWithLabel, | ||||
|     getNotesWithLabels, | ||||
| @@ -131,5 +142,6 @@ module.exports = { | ||||
|     createAttribute, | ||||
|     getAttributeNames, | ||||
|     isAttributeType, | ||||
|     isAttributeDangerous | ||||
| }; | ||||
|     isAttributeDangerous, | ||||
|     getBuiltinAttributeNames | ||||
| }; | ||||
|   | ||||
| @@ -7,7 +7,9 @@ const dataDir = require('./data_dir'); | ||||
| const log = require('./log'); | ||||
| const sqlInit = require('./sql_init'); | ||||
| const syncMutexService = require('./sync_mutex'); | ||||
| const attributeService = require('./attributes'); | ||||
| const cls = require('./cls'); | ||||
| const utils = require('./utils'); | ||||
| const sqlite = require('sqlite'); | ||||
| const sqlite3 = require('sqlite3'); | ||||
|  | ||||
| @@ -96,15 +98,22 @@ async function anonymize() { | ||||
|  | ||||
|     await db.run("UPDATE api_tokens SET token = 'API token value'"); | ||||
|     await db.run("UPDATE notes SET title = 'title'"); | ||||
|     await db.run("UPDATE note_contents SET content = 'text'"); | ||||
|     await db.run("UPDATE note_contents SET content = 'text' WHERE content IS NOT NULL"); | ||||
|     await db.run("UPDATE note_revisions SET title = 'title'"); | ||||
|     await db.run("UPDATE note_revision_contents SET content = 'title'"); | ||||
|     await db.run("UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label'"); | ||||
|     await db.run("UPDATE attributes SET name = 'name' WHERE type = 'relation' AND name != 'template'"); | ||||
|     await db.run("UPDATE note_revision_contents SET content = 'text' WHERE content IS NOT NULL"); | ||||
|  | ||||
|     // we want to delete all non-builtin attributes because they can contain sensitive names and values | ||||
|     // on the other hand builtin/system attrs should not contain any sensitive info | ||||
|     const builtinAttrs = attributeService.getBuiltinAttributeNames().map(name => "'" + utils.sanitizeSql(name) + "'").join(', '); | ||||
|  | ||||
|     await db.run(`UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label' AND name NOT IN(${builtinAttrs})`); | ||||
|     await db.run(`UPDATE attributes SET name = 'name' WHERE type = 'relation' AND name NOT IN (${builtinAttrs})`); | ||||
|     await db.run("UPDATE branches SET prefix = 'prefix' WHERE prefix IS NOT NULL"); | ||||
|     await db.run(`UPDATE options SET value = 'anonymized' WHERE name IN  | ||||
|                     ('documentId', 'documentSecret', 'encryptedDataKey', 'passwordVerificationHash',  | ||||
|                      'passwordVerificationSalt', 'passwordDerivedKeySalt', 'username', 'syncServerHost', 'syncProxy')`); | ||||
|                     ('documentId', 'documentSecret', 'encryptedDataKey',  | ||||
|                      'passwordVerificationHash', 'passwordVerificationSalt',  | ||||
|                      'passwordDerivedKeySalt', 'username', 'syncServerHost', 'syncProxy')  | ||||
|                       AND value != ''`); | ||||
|     await db.run("VACUUM"); | ||||
|  | ||||
|     await db.close(); | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| module.exports = { buildDate:"2020-06-03T14:30:07+02:00", buildRevision: "c1fd9825aa6087b5061cdede5dba3f7f9dc62c31" }; | ||||
| module.exports = { buildDate:"2020-06-08T10:43:12+02:00", buildRevision: "4426362799448b6228eedf20e7fc179ce4b3f860" }; | ||||
|   | ||||
| @@ -47,7 +47,7 @@ async function importTar(taskContext, fileBuffer, importRootNote) { | ||||
|  | ||||
|         return noteIdMap[origNoteId]; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     function getMeta(filePath) { | ||||
|         if (!metaFile) { | ||||
|             return {}; | ||||
| @@ -425,7 +425,7 @@ async function importTar(taskContext, fileBuffer, importRootNote) { | ||||
|             } | ||||
|  | ||||
|             for (const noteId in createdNoteIds) { // now the noteIds are unique | ||||
|                 await noteService.scanForLinks(await repository.getNotes(noteId)); | ||||
|                 await noteService.scanForLinks(await repository.getNote(noteId)); | ||||
|  | ||||
|                 if (!metaFile) { | ||||
|                     // if there's no meta file then the notes are created based on the order in that tar file but that | ||||
| @@ -459,4 +459,4 @@ async function importTar(taskContext, fileBuffer, importRootNote) { | ||||
|  | ||||
| module.exports = { | ||||
|     importTar | ||||
| }; | ||||
| }; | ||||
|   | ||||
| @@ -454,7 +454,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | ||||
|     }); | ||||
|  | ||||
|     for (const noteId in createdNoteIds) { // now the noteIds are unique | ||||
|         await noteService.scanForLinks(await repository.getNotes(noteId)); | ||||
|         await noteService.scanForLinks(await repository.getNote(noteId)); | ||||
|  | ||||
|         if (!metaFile) { | ||||
|             // if there's no meta file then the notes are created based on the order in that tar file but that | ||||
| @@ -481,4 +481,4 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | ||||
|  | ||||
| module.exports = { | ||||
|     importZip | ||||
| }; | ||||
| }; | ||||
|   | ||||
| @@ -205,6 +205,14 @@ function formatDownloadTitle(filename, type, mime) { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (mime === 'application/octet-stream') { | ||||
|             // we didn't find any good guess for this one, it will be better to just return | ||||
|             // the current name without fake extension. It's possible that the title still preserves to correct | ||||
|             // extension too | ||||
|  | ||||
|             return filename; | ||||
|         } | ||||
|  | ||||
|         return filename + '.' + extensions[0]; | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user