Compare commits

..

20 Commits

Author SHA1 Message Date
zadam
e9a2cacb5b release 0.58.8 2023-02-13 21:50:54 +01:00
zadam
17085e5578 fix hidden subtree appearing after collapse, #2922 2023-02-13 15:09:57 +01:00
zadam
fc7da015fe fix image display with slash in title, closes #3591 2023-02-10 10:23:02 +01:00
zadam
adf222b5e8 fix consistency check to re-parent note from search parent to root 2023-02-10 10:12:58 +01:00
zadam
ade22ea825 recover notes into root only if no other valid branch exists, #3590 2023-02-10 10:09:06 +01:00
zadam
2c8fb90ecb Merge pull request #3590 from eliandoran/feature/fix_broken_reference_check
Fix broken reference consistency check
2023-02-10 09:58:24 +01:00
zadam
c67644a2e3 fix filing entity changes for deleted notes 2023-02-10 09:16:32 +01:00
zadam
346f6edd7e fix filing entity changes for deleted notes 2023-02-10 08:55:26 +01:00
Elian Doran
92f586486f Fix broken reference consistency check
If a branch was identified with a missing parent, the branch would be
moved to root. However, the ID of the branch would be changed as a
result of that, and this resulted in the creation of a new branch
instead of updating the old one. Deleting the old one first solves the
issue.
2023-02-05 01:12:13 +02:00
zadam
e7c6d912a4 fix undeleting notes with a relation, closes #3583 2023-02-01 23:06:55 +01:00
zadam
290f7e2101 fix sharing of templates, #3562 2023-02-01 22:58:40 +01:00
zadam
5e0fbea3b3 fix disabled: on attribtue changed script in demo document, fixes #3564 2023-02-01 07:29:37 +01:00
zadam
7b2c3afe4c always open the note in URL, closes #3571 2023-01-31 23:20:11 +01:00
zadam
2e181d0fb1 toggling the left pane will toggle the focus between tree and detail, #3563 2023-01-31 22:35:01 +01:00
zadam
68a03211ce fix padding on launcher when collapsed 2023-01-31 22:25:46 +01:00
zadam
f2a19c56b1 add excludeFromNoteMap to a day template instead of journal root, #3547 2023-01-21 11:13:40 +01:00
zadam
a98fd509c6 release 0.58.7 2023-01-17 23:14:58 +01:00
zadam
a3149aecf4 fix migration for DBs which did not have _hidden tree created, closes #3536 2023-01-17 23:13:21 +01:00
zadam
ef825371cf fix "root" note in autocomplete when hoisted into another subtree 2023-01-17 21:15:05 +01:00
zadam
b567775129 small fixes of steel blue theme 2023-01-17 21:06:30 +01:00
23 changed files with 115 additions and 48 deletions

Binary file not shown.

View File

@@ -7,6 +7,6 @@ module.exports = () => {
beccaLoader.load(); beccaLoader.load();
// make sure the hidden subtree exists since the subsequent migrations we will move some existing notes into it (share...) // make sure the hidden subtree exists since the subsequent migrations we will move some existing notes into it (share...)
// in previous releases hidden subtree was created lazily // in previous releases hidden subtree was created lazily
hiddenSubtreeService.checkHiddenSubtree(); hiddenSubtreeService.checkHiddenSubtree(true);
}); });
}; };

5
package-lock.json generated
View File

@@ -1,12 +1,11 @@
{ {
"name": "trilium", "name": "trilium",
"version": "0.58.5", "version": "0.58.7",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "trilium", "version": "0.58.7",
"version": "0.58.5",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "trilium", "name": "trilium",
"productName": "Trilium Notes", "productName": "Trilium Notes",
"description": "Trilium Notes", "description": "Trilium Notes",
"version": "0.58.6", "version": "0.58.8",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"main": "electron.js", "main": "electron.js",
"bin": { "bin": {

View File

@@ -152,6 +152,10 @@ class Becca {
.replace('_', '') .replace('_', '')
); );
if (!(camelCaseEntityName in this)) {
throw new Error(`Unknown entity name '${camelCaseEntityName}' (original argument '${entityName}')`);
}
return this[camelCaseEntityName][entityId]; return this[camelCaseEntityName][entityId];
} }

View File

@@ -83,10 +83,8 @@ function getNoteTitleArrayForPath(notePathArray) {
throw new Error(`${notePathArray} is not an array.`); throw new Error(`${notePathArray} is not an array.`);
} }
const hoistedNoteId = cls.getHoistedNoteId(); if (notePathArray.length === 1) {
return [getNoteTitle(notePathArray[0])];
if (notePathArray.length === 1 && notePathArray[0] === hoistedNoteId) {
return [getNoteTitle(hoistedNoteId)];
} }
const titles = []; const titles = [];
@@ -95,6 +93,7 @@ function getNoteTitleArrayForPath(notePathArray) {
let hoistedNotePassed = false; let hoistedNotePassed = false;
// this is a notePath from outside of hoisted subtree so full title path needs to be returned // this is a notePath from outside of hoisted subtree so full title path needs to be returned
const hoistedNoteId = cls.getHoistedNoteId();
const outsideOfHoistedSubtree = !notePathArray.includes(hoistedNoteId); const outsideOfHoistedSubtree = !notePathArray.includes(hoistedNoteId);
for (const noteId of notePathArray) { for (const noteId of notePathArray) {

View File

@@ -80,14 +80,14 @@ class AbstractEntity {
* *
* @returns {this} * @returns {this}
*/ */
save() { save(opts = {}) {
const entityName = this.constructor.entityName; const entityName = this.constructor.entityName;
const primaryKeyName = this.constructor.primaryKeyName; const primaryKeyName = this.constructor.primaryKeyName;
const isNewEntity = !this[primaryKeyName]; const isNewEntity = !this[primaryKeyName];
if (this.beforeSaving) { if (this.beforeSaving) {
this.beforeSaving(); this.beforeSaving(opts);
} }
const pojo = this.getPojoToSave(); const pojo = this.getPojoToSave();

View File

@@ -176,8 +176,10 @@ class Attribute extends AbstractEntity {
return !(this.attributeId in this.becca.attributes); return !(this.attributeId in this.becca.attributes);
} }
beforeSaving() { beforeSaving(opts = {}) {
if (!opts.skipValidation) {
this.validate(); this.validate();
}
this.name = sanitizeAttributeName(this.name); this.name = sanitizeAttributeName(this.name);

View File

@@ -79,11 +79,12 @@ export default class TabManager extends Component {
filteredTabs = filteredTabs.filter(tab => tab.active); filteredTabs = filteredTabs.filter(tab => tab.active);
} }
if (filteredTabs.length === 0) { // resolve before opened tabs can change this
const [notePath] = treeService.getHashValueFromAddress(); const [notePathInUrl, ntxIdInUrl] = treeService.getHashValueFromAddress();
if (filteredTabs.length === 0) {
filteredTabs.push({ filteredTabs.push({
notePath: notePath || 'root', notePath: notePathInUrl || 'root',
active: true, active: true,
hoistedNoteId: glob.extraHoistedNoteId || 'root' hoistedNoteId: glob.extraHoistedNoteId || 'root'
}); });
@@ -95,17 +96,14 @@ export default class TabManager extends Component {
await this.tabsUpdate.allowUpdateWithoutChange(async () => { await this.tabsUpdate.allowUpdateWithoutChange(async () => {
for (const tab of filteredTabs) { for (const tab of filteredTabs) {
await this.openContextWithNote(tab.notePath, tab.active, tab.ntxId, tab.hoistedNoteId, tab.mainNtxId); await this.openContextWithNote(tab.notePath, tab.active, tab.ntxId, tab.hoistedNoteId, tab.mainNtxId);
} }
}); });
// if there's notePath in the URL, make sure it's open and active // if there's notePath in the URL, make sure it's open and active
// (useful, for e.g. opening clipped notes from clipper or opening link in an extra window) // (useful, for e.g. opening clipped notes from clipper or opening link in an extra window)
if (treeService.isNotePathInAddress()) { if (notePathInUrl) {
const [notePath, ntxId] = treeService.getHashValueFromAddress(); await appContext.tabManager.switchToNoteContext(ntxIdInUrl, notePathInUrl);
await appContext.tabManager.switchToNoteContext(ntxId, notePath);
} }
} }
catch (e) { catch (e) {

View File

@@ -688,7 +688,7 @@ class NoteShort {
return promotedAttrs; return promotedAttrs;
} }
hasAncestor(ancestorNoteId, visitedNoteIds = null) { hasAncestor(ancestorNoteId, followTemplates = false, visitedNoteIds = null) {
if (this.noteId === ancestorNoteId) { if (this.noteId === ancestorNoteId) {
return true; return true;
} }
@@ -702,14 +702,16 @@ class NoteShort {
visitedNoteIds.add(this.noteId); visitedNoteIds.add(this.noteId);
if (followTemplates) {
for (const templateNote of this.getTemplateNotes()) { for (const templateNote of this.getTemplateNotes()) {
if (templateNote.hasAncestor(ancestorNoteId, visitedNoteIds)) { if (templateNote.hasAncestor(ancestorNoteId, followTemplates, visitedNoteIds)) {
return true; return true;
} }
} }
}
for (const parentNote of this.getParentNotes()) { for (const parentNote of this.getParentNotes()) {
if (parentNote.hasAncestor(ancestorNoteId, visitedNoteIds)) { if (parentNote.hasAncestor(ancestorNoteId, followTemplates, visitedNoteIds)) {
return true; return true;
} }
} }

View File

@@ -50,7 +50,7 @@ function isAffecting(attrRow, affectedNote) {
if (this.isInheritable) { if (this.isInheritable) {
for (const owningNote of owningNotes) { for (const owningNote of owningNotes) {
if (owningNote.hasAncestor(attrNote.noteId)) { if (owningNote.hasAncestor(attrNote.noteId, true)) {
return true; return true;
} }
} }

View File

@@ -242,6 +242,7 @@ const ATTR_HELP = {
"keepCurrentHoisting": "Opening this link won't change hoisting even if the note is not displayable in the current hoisted subtree.", "keepCurrentHoisting": "Opening this link won't change hoisting even if the note is not displayable in the current hoisted subtree.",
"executeButton": "Title of the button which will execute the current code note", "executeButton": "Title of the button which will execute the current code note",
"executeDescription": "Longer description of the current code note displayed together with the execute button", "executeDescription": "Longer description of the current code note displayed together with the execute button",
"excludeFromNoteMap": "Notes with this label will be hidden from the Note Map"
}, },
"relation": { "relation": {
"runOnNoteCreation": "executes when note is created on backend. Use this relation if you want to run the script for all notes created under a specific subtree. In that case, create it on the subtree root note and make it inheritable. A new note created within the subtree (any depth) will trigger the script.", "runOnNoteCreation": "executes when note is created on backend. Use this relation if you want to run the script for all notes created under a specific subtree. In that case, create it on the subtree root note and make it inheritable. A new note created within the subtree (any depth) will trigger the script.",

View File

@@ -303,7 +303,7 @@ export default class GlobalMenuWidget extends BasicWidget {
const resp = await fetch(RELEASES_API_URL); const resp = await fetch(RELEASES_API_URL);
const data = await resp.json(); const data = await resp.json();
return data.tag_name.substring(1); return data?.tag_name?.substring(1);
} }
downloadLatestVersionCommand() { downloadLatestVersionCommand() {

View File

@@ -4,7 +4,6 @@ const TPL = `
<div class="dropdown right-dropdown-widget dropright"> <div class="dropdown right-dropdown-widget dropright">
<style> <style>
.right-dropdown-widget { .right-dropdown-widget {
width: 53px;
height: 53px; height: 53px;
} }
</style> </style>

View File

@@ -1,5 +1,6 @@
import options from "../../services/options.js"; import options from "../../services/options.js";
import FlexContainer from "./flex_container.js"; import FlexContainer from "./flex_container.js";
import appContext from "../../components/app_context.js";
export default class LeftPaneContainer extends FlexContainer { export default class LeftPaneContainer extends FlexContainer {
constructor() { constructor() {
@@ -16,7 +17,15 @@ export default class LeftPaneContainer extends FlexContainer {
entitiesReloadedEvent({loadResults}) { entitiesReloadedEvent({loadResults}) {
if (loadResults.isOptionReloaded("leftPaneVisible")) { if (loadResults.isOptionReloaded("leftPaneVisible")) {
this.toggleInt(this.isEnabled()); const visible = this.isEnabled();
this.toggleInt(visible);
if (visible) {
this.triggerEvent('focusTree');
} else {
const activeNoteContext = appContext.tabManager.getActiveContext();
this.triggerEvent('focusOnDetail', {ntxId: activeNoteContext.ntxId});
}
} }
} }
} }

View File

@@ -832,6 +832,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
} }
}); });
await this.filterHoistedBranch();
const activeNode = await this.getNodeFromPath(appContext.tabManager.getActiveContextNotePath()); const activeNode = await this.getNodeFromPath(appContext.tabManager.getActiveContextNotePath());
if (activeNode) { if (activeNode) {
@@ -887,6 +889,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
} }
} }
async focusTreeEvent() {
this.tree.$container.focus();
this.tree.setFocus(true);
}
/** @returns {FancytreeNode} */ /** @returns {FancytreeNode} */
async getNodeFromPath(notePath, expand = false, logErrors = true) { async getNodeFromPath(notePath, expand = false, logErrors = true) {
utils.assertArguments(notePath); utils.assertArguments(notePath);
@@ -1074,6 +1081,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
} }
} }
}, false); }, false);
this.filterHoistedBranch();
}, 600 * 1000); }, 600 * 1000);
} }

View File

@@ -90,7 +90,7 @@ class ImageTypeWidget extends TypeWidget {
} }
async doRefresh(note) { async doRefresh(note) {
this.$imageView.prop("src", `api/images/${note.noteId}/${note.title}`); this.$imageView.prop("src", `api/images/${note.noteId}/${encodeURIComponent(note.title)}`);
} }
copyImageReferenceToClipboardEvent({ntxId}) { copyImageReferenceToClipboardEvent({ntxId}) {

View File

@@ -871,12 +871,11 @@ body {
#launcher-pane .launcher-button { #launcher-pane .launcher-button {
font-size: 150%; font-size: 150%;
display: inline-block; display: inline-block;
padding: 15px 15px; padding: 13px 13px;
cursor: pointer; cursor: pointer;
border: none; border: none;
color: var(--launcher-pane-text-color); color: var(--launcher-pane-text-color);
background-color: var(--launcher-pane-background-color); background-color: var(--launcher-pane-background-color);
width: 53px;
height: 53px; height: 53px;
} }

View File

@@ -1 +1 @@
module.exports = { buildDate:"2023-01-16T22:39:28+01:00", buildRevision: "9fd0b85ff2be264be35ec2052c956b654f0dac9e" }; module.exports = { buildDate:"2023-02-13T21:50:54+01:00", buildRevision: "17085e5578d2a20a77a6ade058f74e6d5b798ecc" };

View File

@@ -148,13 +148,28 @@ class ConsistencyChecks {
AND notes.noteId IS NULL`, AND notes.noteId IS NULL`,
({branchId, parentNoteId}) => { ({branchId, parentNoteId}) => {
if (this.autoFix) { if (this.autoFix) {
const branch = becca.getBranch(branchId); // Delete the old branch and recreate it with root as parent.
branch.parentNoteId = 'root'; const oldBranch = becca.getBranch(branchId);
branch.save(); const noteId = oldBranch.noteId;
oldBranch.markAsDeleted("missing-parent");
let message = `Branch '${branchId}' was was missing parent note '${parentNoteId}', so it was deleted. `;
if (becca.getNote(noteId).getParentBranches().length === 0) {
const newBranch = new Branch({
parentNoteId: 'root',
noteId: noteId,
prefix: 'recovered'
}).save();
message += `${newBranch.branchId} was created in the root instead.`;
} else {
message += `There is one or more valid branches, so no new one will be created as a replacement.`;
}
this.reloadNeeded = true; this.reloadNeeded = true;
logFix(`Branch '${branchId}' was set to root parent since it was referencing missing parent note '${parentNoteId}'`); logFix(message);
} else { } else {
logError(`Branch '${branchId}' references missing parent note '${parentNoteId}'`); logError(`Branch '${branchId}' references missing parent note '${parentNoteId}'`);
} }
@@ -428,10 +443,17 @@ class ConsistencyChecks {
const branches = branchIds.map(branchId => becca.getBranch(branchId)); const branches = branchIds.map(branchId => becca.getBranch(branchId));
for (const branch of branches) { for (const branch of branches) {
branch.parentNoteId = 'root'; // delete the old wrong branch
branch.save(); branch.markAsDeleted("parent-is-search");
logFix(`Child branch '${branch.branchId}' has been moved to root since it was a child of a search note '${parentNoteId}'`) // create a replacement branch in root parent
new Branch({
parentNoteId: 'root',
noteId: branch.noteId,
prefix: 'recovered'
}).save();
logFix(`Note '${branch.noteId}' has been moved to root since it was a child of a search note '${parentNoteId}'`)
} }
this.reloadNeeded = true; this.reloadNeeded = true;

View File

@@ -100,15 +100,38 @@ function fillEntityChanges(entityName, entityPrimaryKey, condition = '') {
if (existingRows === 0) { if (existingRows === 0) {
createdCount++; createdCount++;
let hash;
let utcDateChanged;
let isSynced;
if (entityName.endsWith("_contents")) {
// FIXME: hacky, not sure if it might cause some problems
hash = "fake value";
utcDateChanged = dateUtils.utcNowDateTime();
isSynced = true; // contents are always synced
} else {
const entity = becca.getEntity(entityName, entityId); const entity = becca.getEntity(entityName, entityId);
if (entity) {
hash = entity?.generateHash() || "|deleted";
utcDateChanged = entity?.getUtcDateChanged() || dateUtils.utcNowDateTime();
isSynced = entityName !== 'options' || !!entity?.isSynced;
} else {
// entity might be null (not present in becca) when it's deleted
// FIXME: hacky, not sure if it might cause some problems
hash = "deleted";
utcDateChanged = dateUtils.utcNowDateTime();
isSynced = true; // deletable (the ones with isDeleted) entities are synced
}
}
addEntityChange({ addEntityChange({
entityName, entityName,
entityId, entityId,
hash: entity.generateHash(), hash: hash,
isErased: false, isErased: false,
utcDateChanged: entity.getUtcDateChanged(), utcDateChanged: utcDateChanged,
isSynced: entityName !== 'options' || !!entity.isSynced isSynced: isSynced
}); });
} }
} }

View File

@@ -234,8 +234,8 @@ const HIDDEN_SUBTREE_DEFINITION = {
] ]
}; };
function checkHiddenSubtree() { function checkHiddenSubtree(force = false) {
if (!migrationService.isDbUpToDate()) { if (!force && !migrationService.isDbUpToDate()) {
// on-delete hook might get triggered during some future migration and cause havoc // on-delete hook might get triggered during some future migration and cause havoc
log.info("Will not check hidden subtree until migration is finished."); log.info("Will not check hidden subtree until migration is finished.");
return; return;

View File

@@ -661,7 +661,8 @@ function undeleteBranch(branchId, deleteId, taskContext) {
OR (type = 'relation' AND value = ?))`, [deleteId, note.noteId, note.noteId]); OR (type = 'relation' AND value = ?))`, [deleteId, note.noteId, note.noteId]);
for (const attribute of attributes) { for (const attribute of attributes) {
new Attribute(attribute).save(); // relation might point to a note which hasn't been undeleted yet and would thus throw up
new Attribute(attribute).save({skipValidation: true});
} }
const childBranchIds = sql.getColumn(` const childBranchIds = sql.getColumn(`