mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 18:05:55 +01:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
			algolia_v1
			...
			v0.48.9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 265401775b | ||
|  | 10a5773c66 | ||
|  | 1e8472266f | ||
|  | b30792a3da | ||
|  | 26602e8226 | ||
|  | b8eeb0371c | ||
|  | b1c4737e78 | ||
|  | e29aee1aae | ||
|  | 1aff42f453 | ||
|  | a098630e09 | ||
|  | 074eb1c02f | 
							
								
								
									
										11072
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11072
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,7 +2,7 @@ | ||||
|   "name": "trilium", | ||||
|   "productName": "Trilium Notes", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.48.8", | ||||
|   "version": "0.48.9", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "main": "electron.js", | ||||
|   "bin": { | ||||
|   | ||||
| @@ -42,7 +42,7 @@ class NoteBuilder { | ||||
|     } | ||||
|  | ||||
|     child(childNoteBuilder, prefix = "") { | ||||
|         new Branch(becca, { | ||||
|         new Branch({ | ||||
|             branchId: id(), | ||||
|             noteId: childNoteBuilder.note.noteId, | ||||
|             parentNoteId: this.note.noteId, | ||||
|   | ||||
| @@ -37,7 +37,7 @@ describe("Parser", () => { | ||||
|         expect(rootExp.constructor.name).toEqual("AndExp"); | ||||
|         expect(rootExp.subExpressions[0].constructor.name).toEqual("PropertyComparisonExp"); | ||||
|         expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp"); | ||||
|         expect(rootExp.subExpressions[1].subExpressions[0].constructor.name).toEqual("BeccaFlatTextExp"); | ||||
|         expect(rootExp.subExpressions[1].subExpressions[0].constructor.name).toEqual("NoteFlatTextExp"); | ||||
|         expect(rootExp.subExpressions[1].subExpressions[0].tokens).toEqual(["hello", "hi"]); | ||||
|     }); | ||||
|  | ||||
| @@ -55,7 +55,7 @@ describe("Parser", () => { | ||||
|  | ||||
|         const subs = rootExp.subExpressions[1].subExpressions; | ||||
|  | ||||
|         expect(subs[0].constructor.name).toEqual("BeccaFlatTextExp"); | ||||
|         expect(subs[0].constructor.name).toEqual("NoteFlatTextExp"); | ||||
|         expect(subs[0].tokens).toEqual(["hello", "hi"]); | ||||
|  | ||||
|         expect(subs[1].constructor.name).toEqual("NoteContentProtectedFulltextExp"); | ||||
| @@ -182,7 +182,7 @@ describe("Parser", () => { | ||||
|         expect(firstSub.propertyName).toEqual('isArchived'); | ||||
|  | ||||
|         expect(secondSub.constructor.name).toEqual("OrExp"); | ||||
|         expect(secondSub.subExpressions[0].constructor.name).toEqual("BeccaFlatTextExp"); | ||||
|         expect(secondSub.subExpressions[0].constructor.name).toEqual("NoteFlatTextExp"); | ||||
|         expect(secondSub.subExpressions[0].tokens).toEqual(["hello"]); | ||||
|  | ||||
|         expect(thirdSub.constructor.name).toEqual("LabelComparisonExp"); | ||||
|   | ||||
| @@ -13,7 +13,7 @@ describe("Search", () => { | ||||
|         becca.reset(); | ||||
|  | ||||
|         rootNote = new NoteBuilder(new Note({noteId: 'root', title: 'root', type: 'text'})); | ||||
|         new Branch(becca, {branchId: 'root', noteId: 'root', parentNoteId: 'none', notePosition: 10}); | ||||
|         new Branch({branchId: 'root', noteId: 'root', parentNoteId: 'none', notePosition: 10}); | ||||
|     }); | ||||
|  | ||||
|     it("simple path match", () => { | ||||
| @@ -157,6 +157,21 @@ describe("Search", () => { | ||||
|         expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy(); | ||||
|     }); | ||||
|  | ||||
|     it("inherited label comparison", () => { | ||||
|         rootNote | ||||
|             .child(note("Europe") | ||||
|                 .label('country', '', true) | ||||
|                 .child(note("Austria")) | ||||
|                 .child(note("Czech Republic")) | ||||
|             ); | ||||
|  | ||||
|         const searchContext = new SearchContext(); | ||||
|  | ||||
|         const searchResults = searchService.findResultsWithQuery('austria #country', searchContext); | ||||
|         expect(searchResults.length).toEqual(1); | ||||
|         expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy(); | ||||
|     }); | ||||
|  | ||||
|     it("numeric label comparison fallback to string comparison", () => { | ||||
|         // dates should not be coerced into numbers which would then give wrong numbers | ||||
|  | ||||
| @@ -218,7 +233,7 @@ describe("Search", () => { | ||||
|         test("#month = month", 1); | ||||
|         test("#month = 'MONTH'", 0); | ||||
|  | ||||
|         test("note.dateCreated =* month", 1); | ||||
|         test("note.dateCreated =* month", 2); | ||||
|  | ||||
|         test("#date = TODAY", 1); | ||||
|         test("#date = today", 1); | ||||
| @@ -337,11 +352,11 @@ describe("Search", () => { | ||||
|  | ||||
|         const searchContext = new SearchContext(); | ||||
|  | ||||
|         let searchResults = searchService.findResultsWithQuery('#city AND note.getAncestors().title = Europe', searchContext); | ||||
|         let searchResults = searchService.findResultsWithQuery('#city AND note.ancestors.title = Europe', searchContext); | ||||
|         expect(searchResults.length).toEqual(1); | ||||
|         expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy(); | ||||
|  | ||||
|         searchResults = searchService.findResultsWithQuery('#city AND note.getAncestors().title = Asia', searchContext); | ||||
|         searchResults = searchService.findResultsWithQuery('#city AND note.ancestors.title = Asia', searchContext); | ||||
|         expect(searchResults.length).toEqual(1); | ||||
|         expect(findNoteByTitle(searchResults, "Taipei")).toBeTruthy(); | ||||
|     }); | ||||
|   | ||||
| @@ -84,7 +84,7 @@ class Branch extends AbstractEntity { | ||||
|     /** @returns {Note} */ | ||||
|     get childNote() { | ||||
|         if (!(this.noteId in this.becca.notes)) { | ||||
|             // entities can come out of order in sync, create skeleton which will be filled later | ||||
|             // entities can come out of order in sync/import, create skeleton which will be filled later | ||||
|             this.becca.addNote(this.noteId, new Note({noteId: this.noteId})); | ||||
|         } | ||||
|  | ||||
| @@ -98,7 +98,7 @@ class Branch extends AbstractEntity { | ||||
|     /** @returns {Note} */ | ||||
|     get parentNote() { | ||||
|         if (!(this.parentNoteId in this.becca.notes)) { | ||||
|             // entities can come out of order in sync, create skeleton which will be filled later | ||||
|             // entities can come out of order in sync/import, create skeleton which will be filled later | ||||
|             this.becca.addNote(this.parentNoteId, new Note({noteId: this.parentNoteId})); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -34,8 +34,8 @@ const TPL = ` | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-group"> | ||||
|         <label for="image-jpeg-quality">JPEG quality (0 - worst quality, 100 best quality, 50 - 85 is recommended)</label> | ||||
|         <input class="form-control" id="image-jpeg-quality" min="0" max="100" type="number"> | ||||
|         <label for="image-jpeg-quality">JPEG quality (10 - worst quality, 100 best quality, 50 - 85 is recommended)</label> | ||||
|         <input class="form-control" id="image-jpeg-quality" min="10" max="100" type="number"> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
|   | ||||
| @@ -213,9 +213,13 @@ export default class Entrypoints extends Component { | ||||
|         } else if (note.mime.endsWith("env=backend")) { | ||||
|             await server.post('script/run/' + note.noteId); | ||||
|         } else if (note.mime === 'text/x-sqlite;schema=trilium') { | ||||
|             const {results} = await server.post("sql/execute/" + note.noteId); | ||||
|             const resp = await server.post("sql/execute/" + note.noteId); | ||||
|  | ||||
|             await appContext.triggerEvent('sqlQueryResults', {ntxId: ntxId, results: results}); | ||||
|             if (!resp.success) { | ||||
|                 alert("Error occurred while executing SQL query: " + resp.message); | ||||
|             } | ||||
|  | ||||
|             await appContext.triggerEvent('sqlQueryResults', {ntxId: ntxId, results: resp.results}); | ||||
|         } | ||||
|  | ||||
|         toastService.showMessage("Note executed"); | ||||
|   | ||||
| @@ -83,6 +83,10 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr | ||||
|                 if (someNotePath) { // in case it's root the path may be empty | ||||
|                     const pathToRoot = someNotePath.split("/").reverse().slice(1); | ||||
|  | ||||
|                     if (!pathToRoot.includes("root")) { | ||||
|                         pathToRoot.push('root'); | ||||
|                     } | ||||
|  | ||||
|                     for (const noteId of pathToRoot) { | ||||
|                         effectivePathSegments.push(noteId); | ||||
|                     } | ||||
|   | ||||
| @@ -44,7 +44,10 @@ export default class ButtonWidget extends NoteContextAwareWidget { | ||||
|         this.$widget.tooltip({ | ||||
|             html: true, | ||||
|             title: () => { | ||||
|                 const title = this.settings.title; | ||||
|                 const title = typeof this.settings.title === "function" | ||||
|                     ? this.settings.title() | ||||
|                     : this.settings.title; | ||||
|  | ||||
|                 const action = actions.find(act => act.actionName === this.settings.command); | ||||
|  | ||||
|                 if (action && action.effectiveShortcuts.length > 0) { | ||||
|   | ||||
| @@ -18,7 +18,12 @@ export default class OpenNoteButtonWidget extends ButtonWidget { | ||||
|             } | ||||
|  | ||||
|             this.icon(note.getIcon()); | ||||
|             this.title(note.title); | ||||
|             this.title(() => { | ||||
|                 const n = froca.getNoteFromCache(noteId); | ||||
|  | ||||
|                 // always fresh, always decoded (when protected session is available) | ||||
|                 return n.title; | ||||
|             }); | ||||
|  | ||||
|             this.refreshIcon(); | ||||
|         }); | ||||
|   | ||||
| @@ -84,5 +84,11 @@ export default class EmptyTypeWidget extends TypeWidget { | ||||
|                     .on('click', () => this.triggerCommand('hoistNote', {noteId: workspaceNote.noteId})) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         if (workspaceNotes.length === 0) { | ||||
|             this.$autoComplete | ||||
|                 .trigger('focus') | ||||
|                 .trigger('select'); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| module.exports = { buildDate:"2021-12-13T11:12:31+01:00", buildRevision: "d9550dd59b9b0dff0b229c400cdf6585abcb226a" }; | ||||
| module.exports = { buildDate:"2021-12-22T22:39:24+01:00", buildRevision: "10a5773c66e678c6d2bd3bd9dc9952d5cd76b795" }; | ||||
|   | ||||
| @@ -258,7 +258,8 @@ class ConsistencyChecks { | ||||
|                              FROM branches | ||||
|                              WHERE noteId = ? | ||||
|                                and parentNoteId = ? | ||||
|                                and isDeleted = 0`, [noteId, parentNoteId]); | ||||
|                                and isDeleted = 0 | ||||
|                              ORDER BY utcDateCreated`, [noteId, parentNoteId]); | ||||
|  | ||||
|                     const branches = branchIds.map(branchId => becca.getBranch(branchId)); | ||||
|  | ||||
| @@ -537,6 +538,27 @@ class ConsistencyChecks { | ||||
|                         logError(`Unrecognized entity change id=${id}, entityName=${entityName}, entityId=${entityId}`); | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|         this.findAndFixIssues(` | ||||
|             SELECT  | ||||
|               id, entityId | ||||
|             FROM  | ||||
|               entity_changes  | ||||
|               JOIN ${entityName} ON entityId = ${key}  | ||||
|             WHERE | ||||
|               entity_changes.isErased = 1 | ||||
|               AND entity_changes.entityName = '${entityName}'`, | ||||
|             ({id, entityId}) => { | ||||
|                 if (this.autoFix) { | ||||
|                     sql.execute(`DELETE FROM ${entityName} WHERE ${key} = ?`, [entityId]); | ||||
|  | ||||
|                     this.reloadNeeded = true; | ||||
|  | ||||
|                     logFix(`Erasing entityName=${entityName}, entityId=${entityId} since entity change id=${id} has it as erased.`); | ||||
|                 } else { | ||||
|                     logError(`Entity change id=${id} has entityName=${entityName}, entityId=${entityId} as erased, but it's not.`); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     findEntityChangeIssues() { | ||||
| @@ -603,14 +625,14 @@ class ConsistencyChecks { | ||||
|         this.fixedIssues = false; | ||||
|         this.reloadNeeded = false; | ||||
|  | ||||
|         this.findEntityChangeIssues(); | ||||
|  | ||||
|         this.findBrokenReferenceIssues(); | ||||
|  | ||||
|         this.findExistencyIssues(); | ||||
|  | ||||
|         this.findLogicIssues(); | ||||
|  | ||||
|         this.findEntityChangeIssues(); | ||||
|  | ||||
|         this.findWronglyNamedAttributes(); | ||||
|  | ||||
|         this.findSyncIssues(); | ||||
|   | ||||
| @@ -50,7 +50,13 @@ function exportToZip(taskContext, branch, format, res) { | ||||
|     } | ||||
|  | ||||
|     function getDataFileName(note, baseFileName, existingFileNames) { | ||||
|         const existingExtension = path.extname(baseFileName).toLowerCase(); | ||||
|         let fileName = baseFileName; | ||||
|  | ||||
|         if (fileName.length > 30) { | ||||
|             fileName = fileName.substr(0, 30); | ||||
|         } | ||||
|  | ||||
|         let existingExtension = path.extname(fileName).toLowerCase(); | ||||
|         let newExtension; | ||||
|  | ||||
|         // following two are handled specifically since we always want to have these extensions no matter the automatic detection | ||||
| @@ -67,14 +73,13 @@ function exportToZip(taskContext, branch, format, res) { | ||||
|         else if (existingExtension.length > 0) { // if the page already has an extension, then we'll just keep it | ||||
|             newExtension = null; | ||||
|         } | ||||
|         else { | ||||
|             if (note.mime?.toLowerCase()?.trim() === "image/jpg") { | ||||
|                 newExtension = 'jpg'; | ||||
|             } | ||||
|             else { | ||||
|                 newExtension = mimeTypes.extension(note.mime) || "dat"; | ||||
|             } | ||||
|  | ||||
|         let fileName = baseFileName; | ||||
|  | ||||
|         if (fileName.length > 30) { | ||||
|             fileName = fileName.substr(0, 30); | ||||
|         } | ||||
|  | ||||
|         // if the note is already named with extension (e.g. "jquery"), then it's silly to append exact same extension again | ||||
|   | ||||
| @@ -122,7 +122,11 @@ function saveImage(parentNoteId, uploadBuffer, originalName, shrinkImageSwitch, | ||||
| } | ||||
|  | ||||
| async function shrinkImage(buffer, originalName) { | ||||
|     const jpegQuality = optionService.getOptionInt('imageJpegQuality'); | ||||
|     let jpegQuality = optionService.getOptionInt('imageJpegQuality'); | ||||
|  | ||||
|     if (jpegQuality < 10 || jpegQuality > 100) { | ||||
|         jpegQuality = 75; | ||||
|     } | ||||
|  | ||||
|     let finalImageBuffer; | ||||
|     try { | ||||
|   | ||||
| @@ -351,7 +351,19 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | ||||
|  | ||||
|         let note = becca.getNote(noteId); | ||||
|  | ||||
|         const isProtected = importRootNote.isProtected && protectedSessionService.isProtectedSessionAvailable(); | ||||
|  | ||||
|         if (note) { | ||||
|             // only skeleton was created because of altered order of cloned notes in ZIP, we need to update | ||||
|             // https://github.com/zadam/trilium/issues/2440 | ||||
|             if (note.type === undefined) { | ||||
|                 note.type = type; | ||||
|                 note.mime = mime; | ||||
|                 note.title = noteTitle; | ||||
|                 note.isProtected = isProtected; | ||||
|                 note.save(); | ||||
|             } | ||||
|  | ||||
|             note.setContent(content); | ||||
|         } | ||||
|         else { | ||||
| @@ -367,7 +379,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | ||||
|                 // root notePosition should be ignored since it relates to original document | ||||
|                 // now import root should be placed after existing notes into new parent | ||||
|                 notePosition: (noteMeta && firstNote) ? noteMeta.notePosition : undefined, | ||||
|                 isProtected: importRootNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), | ||||
|                 isProtected: isProtected, | ||||
|             })); | ||||
|  | ||||
|             createdNoteIds[note.noteId] = true; | ||||
|   | ||||
| @@ -732,6 +732,8 @@ function eraseAttributes(attributeIdsToErase) { | ||||
| } | ||||
|  | ||||
| function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) { | ||||
|     // this is important also so that the erased entity changes are sent to the connected clients | ||||
|     sql.transactional(() => { | ||||
|         if (eraseEntitiesAfterTimeInSeconds === null) { | ||||
|             eraseEntitiesAfterTimeInSeconds = optionService.getOptionInt('eraseEntitiesAfterTimeInSeconds'); | ||||
|         } | ||||
| @@ -749,6 +751,7 @@ function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) { | ||||
|         const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]); | ||||
|  | ||||
|         eraseAttributes(attributeIdsToErase); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function eraseNotesWithDeleteId(deleteId) { | ||||
|   | ||||
| @@ -23,7 +23,6 @@ class AttributeExistsExp extends Expression { | ||||
|         for (const attr of attrs) { | ||||
|             const note = attr.note; | ||||
|  | ||||
|             if (inputNoteSet.hasNoteId(note.noteId)) { | ||||
|             if (attr.isInheritable) { | ||||
|                 resultNoteSet.addAll(note.getSubtreeNotesIncludingTemplated()); | ||||
|             } | ||||
| @@ -34,9 +33,8 @@ class AttributeExistsExp extends Expression { | ||||
|                 resultNoteSet.add(note); | ||||
|             } | ||||
|         } | ||||
|         } | ||||
|  | ||||
|         return resultNoteSet; | ||||
|         return resultNoteSet.intersection(inputNoteSet); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ const RelationWhereExp = require('../expressions/relation_where'); | ||||
| const PropertyComparisonExp = require('../expressions/property_comparison'); | ||||
| const AttributeExistsExp = require('../expressions/attribute_exists'); | ||||
| const LabelComparisonExp = require('../expressions/label_comparison'); | ||||
| const BeccaFlatTextExp = require('../expressions/note_cache_flat_text'); | ||||
| const NoteFlatTextExp = require('../expressions/note_flat_text.js'); | ||||
| const NoteContentProtectedFulltextExp = require('../expressions/note_content_protected_fulltext'); | ||||
| const NoteContentUnprotectedFulltextExp = require('../expressions/note_content_unprotected_fulltext'); | ||||
| const OrderByAndLimitExp = require('../expressions/order_by_and_limit'); | ||||
| @@ -31,13 +31,13 @@ function getFulltext(tokens, searchContext) { | ||||
|  | ||||
|     if (!searchContext.fastSearch) { | ||||
|         return new OrExp([ | ||||
|             new BeccaFlatTextExp(tokens), | ||||
|             new NoteFlatTextExp(tokens), | ||||
|             new NoteContentProtectedFulltextExp('*=*', tokens), | ||||
|             new NoteContentUnprotectedFulltextExp('*=*', tokens) | ||||
|         ]); | ||||
|     } | ||||
|     else { | ||||
|         return new BeccaFlatTextExp(tokens); | ||||
|         return new NoteFlatTextExp(tokens); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -34,6 +34,7 @@ function getHiddenRoot() { | ||||
|  | ||||
|     if (!hidden) { | ||||
|         hidden = noteService.createNewNote({ | ||||
|             branchId: 'hidden', | ||||
|             noteId: 'hidden', | ||||
|             title: 'hidden', | ||||
|             type: 'text', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user