mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
more relation events, events are now not triggered on sync changes
This commit is contained in:
parent
df9acd0504
commit
0f106fb96f
@ -6,6 +6,9 @@ const protectedSessionService = require('../services/protected_session');
|
|||||||
const repository = require('../services/repository');
|
const repository = require('../services/repository');
|
||||||
const dateUtils = require('../services/date_utils');
|
const dateUtils = require('../services/date_utils');
|
||||||
|
|
||||||
|
const LABEL = 'label';
|
||||||
|
const RELATION = 'relation';
|
||||||
|
|
||||||
class Note extends Entity {
|
class Note extends Entity {
|
||||||
static get tableName() { return "notes"; }
|
static get tableName() { return "notes"; }
|
||||||
static get primaryKeyName() { return "noteId"; }
|
static get primaryKeyName() { return "noteId"; }
|
||||||
@ -78,6 +81,14 @@ class Note extends Entity {
|
|||||||
return this.__attributeCache;
|
return this.__attributeCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLabels() {
|
||||||
|
return (await this.getAttributes()).filter(attr => attr.type === LABEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRelations() {
|
||||||
|
return (await this.getAttributes()).filter(attr => attr.type === RELATION);
|
||||||
|
}
|
||||||
|
|
||||||
invalidateAttributeCache() {
|
invalidateAttributeCache() {
|
||||||
this.__attributeCache = null;
|
this.__attributeCache = null;
|
||||||
}
|
}
|
||||||
@ -145,55 +156,55 @@ class Note extends Entity {
|
|||||||
this.__attributeCache = filteredAttributes;
|
this.__attributeCache = filteredAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasLabel(name) {
|
async hasAttribute(type, name) {
|
||||||
return !!await this.getLabel(name);
|
return !!await this.getAttribute(type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: this doesn't take into account the possibility to have multi-valued labels!
|
// WARNING: this doesn't take into account the possibility to have multi-valued labels!
|
||||||
async getLabel(name) {
|
async getAttribute(type, name) {
|
||||||
const attributes = await this.getAttributes();
|
const attributes = await this.getAttributes();
|
||||||
|
|
||||||
return attributes.find(attr => attr.type === 'label' && attr.name === name);
|
return attributes.find(attr => attr.type === type && attr.name === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLabelValue(name) {
|
async getAttributeValue(type, name) {
|
||||||
const label = await this.getLabel(name);
|
const attr = await this.getAttribute(type, name);
|
||||||
|
|
||||||
return label ? label.value : null;
|
return attr ? attr.value : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleLabel(enabled, name, value = "") {
|
async toggleAttribute(type, enabled, name, value = "") {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
await this.setLabel(name, value);
|
await this.setAttribute(type, name, value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await this.removeLabel(name, value);
|
await this.removeAttribute(type, name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setLabel(name, value = "") {
|
async setAttribute(type, name, value = "") {
|
||||||
const attributes = await this.getOwnedAttributes();
|
const attributes = await this.getOwnedAttributes();
|
||||||
let label = attributes.find(attr => attr.type === 'label' && attr.value === value);
|
let attr = attributes.find(attr => attr.type === type && attr.value === value);
|
||||||
|
|
||||||
if (!label) {
|
if (!attr) {
|
||||||
label = new Attribute({
|
attr = new Attribute({
|
||||||
noteId: this.noteId,
|
noteId: this.noteId,
|
||||||
type: 'label',
|
type: type,
|
||||||
name: name,
|
name: name,
|
||||||
value: value
|
value: value
|
||||||
});
|
});
|
||||||
|
|
||||||
await label.save();
|
await attr.save();
|
||||||
|
|
||||||
this.invalidateAttributeCache();
|
this.invalidateAttributeCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeLabel(name, value = "") {
|
async removeAttribute(type, name, value = "") {
|
||||||
const attributes = await this.getOwnedAttributes();
|
const attributes = await this.getOwnedAttributes();
|
||||||
|
|
||||||
for (const attribute of attributes) {
|
for (const attribute of attributes) {
|
||||||
if (attribute.type === 'label' && (!value || value === attribute.value)) {
|
if (attribute.type === type && (!value || value === attribute.value)) {
|
||||||
attribute.isDeleted = true;
|
attribute.isDeleted = true;
|
||||||
await attribute.save();
|
await attribute.save();
|
||||||
|
|
||||||
@ -202,6 +213,24 @@ class Note extends Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async hasLabel(name) { return await this.hasAttribute(LABEL, name); }
|
||||||
|
async hasRelation(name) { return await this.hasAttribute(RELATION, name); }
|
||||||
|
|
||||||
|
async getLabel(name) { return await this.getAttribute(LABEL, name); }
|
||||||
|
async getRelation(name) { return await this.getAttribute(RELATION, name); }
|
||||||
|
|
||||||
|
async getLabelValue(name) { return await this.getAttributeValue(LABEL, name); }
|
||||||
|
async getRelationValue(name) { return await this.getAttributeValue(RELATION, name); }
|
||||||
|
|
||||||
|
async toggleLabel(enabled, name, value = "") { return await this.toggleAttribute(LABEL, enabled, name, value); }
|
||||||
|
async toggleRelation(enabled, name, value = "") { return await this.toggleAttribute(RELATION, enabled, name, value); }
|
||||||
|
|
||||||
|
async setLabel(name, value = "") { return await this.setAttribute(LABEL, name, value); }
|
||||||
|
async setRelation(name, value = "") { return await this.setAttribute(RELATION, name, value); }
|
||||||
|
|
||||||
|
async removeLabel(name, value = "") { return await this.removeAttribute(LABEL, name, value); }
|
||||||
|
async removeRelation(name, value = "") { return await this.removeAttribute(RELATION, name, value); }
|
||||||
|
|
||||||
async getRevisions() {
|
async getRevisions() {
|
||||||
return await repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]);
|
return await repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,11 @@ const BUILTIN_ATTRIBUTES = [
|
|||||||
|
|
||||||
// relation names
|
// relation names
|
||||||
{ type: 'relation', name: 'runOnNoteView' },
|
{ type: 'relation', name: 'runOnNoteView' },
|
||||||
|
{ type: 'relation', name: 'runOnNoteCreation' },
|
||||||
{ type: 'relation', name: 'runOnNoteTitleChange' },
|
{ type: 'relation', name: 'runOnNoteTitleChange' },
|
||||||
|
{ type: 'relation', name: 'runOnNoteChange' },
|
||||||
|
{ type: 'relation', name: 'runOnChildNoteCreation' },
|
||||||
|
{ type: 'relation', name: 'runOnAttributeCreation' },
|
||||||
{ type: 'relation', name: 'runOnAttributeChange' },
|
{ type: 'relation', name: 'runOnAttributeChange' },
|
||||||
{ type: 'relation', name: 'inheritAttributes' }
|
{ type: 'relation', name: 'inheritAttributes' }
|
||||||
];
|
];
|
||||||
@ -59,20 +63,19 @@ async function createAttribute(attribute) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getAttributeNames(type, nameLike) {
|
async function getAttributeNames(type, nameLike) {
|
||||||
let names;
|
nameLike = nameLike.toLowerCase();
|
||||||
|
|
||||||
if (!nameLike) {
|
const names = await sql.getColumn(
|
||||||
names = BUILTIN_ATTRIBUTES
|
`SELECT DISTINCT name
|
||||||
.filter(attribute => attribute.type === type)
|
|
||||||
.map(attribute => attribute.name);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
names = await sql.getColumn(
|
|
||||||
`SELECT DISTINCT name
|
|
||||||
FROM attributes
|
FROM attributes
|
||||||
WHERE isDeleted = 0
|
WHERE isDeleted = 0
|
||||||
AND type = ?
|
AND type = ?
|
||||||
AND name LIKE '%${utils.sanitizeSql(nameLike)}%'`, [type]);
|
AND name LIKE '%${utils.sanitizeSql(nameLike)}%'`, [type]);
|
||||||
|
|
||||||
|
for (const attr of BUILTIN_ATTRIBUTES) {
|
||||||
|
if (attr.type === type && attr.name.toLowerCase().includes(nameLike) && !names.includes(attr.name)) {
|
||||||
|
names.push(attr.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
names.sort();
|
names.sort();
|
||||||
|
@ -2,7 +2,9 @@ const log = require('./log');
|
|||||||
|
|
||||||
const NOTE_TITLE_CHANGED = "NOTE_TITLE_CHANGED";
|
const NOTE_TITLE_CHANGED = "NOTE_TITLE_CHANGED";
|
||||||
const ENTER_PROTECTED_SESSION = "ENTER_PROTECTED_SESSION";
|
const ENTER_PROTECTED_SESSION = "ENTER_PROTECTED_SESSION";
|
||||||
|
const ENTITY_CREATED = "ENTITY_CREATED";
|
||||||
const ENTITY_CHANGED = "ENTITY_CHANGED";
|
const ENTITY_CHANGED = "ENTITY_CHANGED";
|
||||||
|
const CHILD_NOTE_CREATED = "CHILD_NOTE_CREATED";
|
||||||
|
|
||||||
const eventListeners = {};
|
const eventListeners = {};
|
||||||
|
|
||||||
@ -33,5 +35,7 @@ module.exports = {
|
|||||||
// event types:
|
// event types:
|
||||||
NOTE_TITLE_CHANGED,
|
NOTE_TITLE_CHANGED,
|
||||||
ENTER_PROTECTED_SESSION,
|
ENTER_PROTECTED_SESSION,
|
||||||
ENTITY_CHANGED
|
ENTITY_CREATED,
|
||||||
|
ENTITY_CHANGED,
|
||||||
|
CHILD_NOTE_CREATED
|
||||||
};
|
};
|
@ -2,12 +2,10 @@ const eventService = require('./events');
|
|||||||
const scriptService = require('./script');
|
const scriptService = require('./script');
|
||||||
const treeService = require('./tree');
|
const treeService = require('./tree');
|
||||||
const messagingService = require('./messaging');
|
const messagingService = require('./messaging');
|
||||||
const repository = require('./repository');
|
|
||||||
const log = require('./log');
|
const log = require('./log');
|
||||||
|
|
||||||
async function runAttachedRelations(note, relationName, originEntity) {
|
async function runAttachedRelations(note, relationName, originEntity) {
|
||||||
const attributes = await note.getAttributes();
|
const runRelations = (await note.getRelations()).filter(relation => relation.name === relationName);
|
||||||
const runRelations = attributes.filter(relation => relation.type === 'relation' && relation.name === relationName);
|
|
||||||
|
|
||||||
for (const relation of runRelations) {
|
for (const relation of runRelations) {
|
||||||
const scriptNote = await relation.getTargetNote();
|
const scriptNote = await relation.getTargetNote();
|
||||||
@ -37,10 +35,24 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
eventService.subscribe(eventService.ENTITY_CHANGED, async ({ entityId, entityName }) => {
|
eventService.subscribe(eventService.ENTITY_CHANGED, async ({ entityName, entity }) => {
|
||||||
if (entityName === 'attributes') {
|
if (entityName === 'attributes') {
|
||||||
const attribute = await repository.getEntityFromName(entityName, entityId);
|
await runAttachedRelations(await entity.getNote(), 'runOnAttributeChange', entity);
|
||||||
|
|
||||||
await runAttachedRelations(await attribute.getNote(), 'runOnAttributeChange', attribute);
|
|
||||||
}
|
}
|
||||||
|
else if (entityName === 'notes') {
|
||||||
|
await runAttachedRelations(entity, 'runOnNoteChange', entity);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eventService.subscribe(eventService.ENTITY_CREATED, async ({ entityName, entity }) => {
|
||||||
|
if (entityName === 'attributes') {
|
||||||
|
await runAttachedRelations(await entity.getNote(), 'runOnAttributeCreation', entity);
|
||||||
|
}
|
||||||
|
else if (entityName === 'notes') {
|
||||||
|
await runAttachedRelations(entity, 'runOnNoteCreation', entity);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eventService.subscribe(eventService.CHILD_NOTE_CREATED, async ({ parentNote, childNote }) => {
|
||||||
|
await runAttachedRelations(parentNote, 'runOnChildNoteCreation', childNote);
|
||||||
});
|
});
|
@ -228,13 +228,13 @@ function getNotePath(noteId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId}) => {
|
eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entity}) => {
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entityName === 'notes') {
|
if (entityName === 'notes') {
|
||||||
const note = await repository.getNote(entityId);
|
const note = entity;
|
||||||
|
|
||||||
if (note.isDeleted) {
|
if (note.isDeleted) {
|
||||||
delete noteTitles[note.noteId];
|
delete noteTitles[note.noteId];
|
||||||
@ -245,7 +245,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (entityName === 'branches') {
|
else if (entityName === 'branches') {
|
||||||
const branch = await repository.getBranch(entityId);
|
const branch = entity;
|
||||||
|
|
||||||
if (childToParent[branch.noteId]) {
|
if (childToParent[branch.noteId]) {
|
||||||
childToParent[branch.noteId] = childToParent[branch.noteId].filter(noteId => noteId !== branch.parentNoteId)
|
childToParent[branch.noteId] = childToParent[branch.noteId].filter(noteId => noteId !== branch.parentNoteId)
|
||||||
@ -266,7 +266,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (entityName === 'attributes') {
|
else if (entityName === 'attributes') {
|
||||||
const attribute = await repository.getAttribute(entityId);
|
const attribute = entity;
|
||||||
|
|
||||||
if (attribute.type === 'label' && attribute.name === 'archived') {
|
if (attribute.type === 'label' && attribute.name === 'archived') {
|
||||||
// we're not using label object directly, since there might be other non-deleted archived label
|
// we're not using label object directly, since there might be other non-deleted archived label
|
||||||
|
@ -35,6 +35,10 @@ async function getNewNotePosition(parentNoteId, noteData) {
|
|||||||
return newNotePos;
|
return newNotePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function triggerChildNoteCreated(childNote, parentNote) {
|
||||||
|
await eventService.emit(eventService.CHILD_NOTE_CREATED, { childNote, parentNote });
|
||||||
|
}
|
||||||
|
|
||||||
async function triggerNoteTitleChanged(note) {
|
async function triggerNoteTitleChanged(note) {
|
||||||
await eventService.emit(eventService.NOTE_TITLE_CHANGED, note);
|
await eventService.emit(eventService.NOTE_TITLE_CHANGED, note);
|
||||||
}
|
}
|
||||||
@ -42,12 +46,10 @@ async function triggerNoteTitleChanged(note) {
|
|||||||
async function createNewNote(parentNoteId, noteData) {
|
async function createNewNote(parentNoteId, noteData) {
|
||||||
const newNotePos = await getNewNotePosition(parentNoteId, noteData);
|
const newNotePos = await getNewNotePosition(parentNoteId, noteData);
|
||||||
|
|
||||||
if (parentNoteId !== 'root') {
|
const parentNote = await repository.getNote(parentNoteId);
|
||||||
const parent = await repository.getNote(parentNoteId);
|
|
||||||
|
|
||||||
noteData.type = noteData.type || parent.type;
|
noteData.type = noteData.type || parentNote.type;
|
||||||
noteData.mime = noteData.mime || parent.mime;
|
noteData.mime = noteData.mime || parentNote.mime;
|
||||||
}
|
|
||||||
|
|
||||||
const note = await new Note({
|
const note = await new Note({
|
||||||
title: noteData.title,
|
title: noteData.title,
|
||||||
@ -66,6 +68,7 @@ async function createNewNote(parentNoteId, noteData) {
|
|||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
await triggerNoteTitleChanged(note);
|
await triggerNoteTitleChanged(note);
|
||||||
|
await triggerChildNoteCreated(note, parentNote);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
note,
|
note,
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const sql = require('./sql');
|
const sql = require('./sql');
|
||||||
const syncTableService = require('../services/sync_table');
|
const syncTableService = require('../services/sync_table');
|
||||||
|
const eventService = require('./events');
|
||||||
|
|
||||||
let entityConstructor;
|
let entityConstructor;
|
||||||
|
|
||||||
@ -56,6 +57,11 @@ async function getOption(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function updateEntity(entity) {
|
async function updateEntity(entity) {
|
||||||
|
const entityName = entity.constructor.tableName;
|
||||||
|
const primaryKeyName = entity.constructor.primaryKeyName;
|
||||||
|
|
||||||
|
const isNewEntity = !entity[primaryKeyName];
|
||||||
|
|
||||||
if (entity.beforeSaving) {
|
if (entity.beforeSaving) {
|
||||||
await entity.beforeSaving();
|
await entity.beforeSaving();
|
||||||
}
|
}
|
||||||
@ -75,12 +81,24 @@ async function updateEntity(entity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await sql.transactional(async () => {
|
await sql.transactional(async () => {
|
||||||
await sql.replace(entity.constructor.tableName, clone);
|
await sql.replace(entityName, clone);
|
||||||
|
|
||||||
const primaryKey = entity[entity.constructor.primaryKeyName];
|
const primaryKey = entity[primaryKeyName];
|
||||||
|
|
||||||
if (entity.isChanged && (entity.constructor.tableName !== 'options' || entity.isSynced)) {
|
if (entity.isChanged && (entityName !== 'options' || entity.isSynced)) {
|
||||||
await syncTableService.addEntitySync(entity.constructor.tableName, primaryKey);
|
await syncTableService.addEntitySync(entityName, primaryKey);
|
||||||
|
|
||||||
|
if (isNewEntity) {
|
||||||
|
await eventService.emit(eventService.ENTITY_CREATED, {
|
||||||
|
entityName,
|
||||||
|
entity
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await eventService.emit(eventService.ENTITY_CHANGED, {
|
||||||
|
entityName,
|
||||||
|
entity
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ const sourceIdService = require('./source_id');
|
|||||||
const dateUtils = require('./date_utils');
|
const dateUtils = require('./date_utils');
|
||||||
const log = require('./log');
|
const log = require('./log');
|
||||||
const cls = require('./cls');
|
const cls = require('./cls');
|
||||||
const eventService = require('./events');
|
|
||||||
|
|
||||||
async function addNoteSync(noteId, sourceId) {
|
async function addNoteSync(noteId, sourceId) {
|
||||||
await addEntitySync("notes", noteId, sourceId)
|
await addEntitySync("notes", noteId, sourceId)
|
||||||
@ -52,11 +51,6 @@ async function addEntitySync(entityName, entityId, sourceId) {
|
|||||||
syncDate: dateUtils.nowDate(),
|
syncDate: dateUtils.nowDate(),
|
||||||
sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId()
|
sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId()
|
||||||
});
|
});
|
||||||
|
|
||||||
await eventService.emit(eventService.ENTITY_CHANGED, {
|
|
||||||
entityName,
|
|
||||||
entityId
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cleanupSyncRowsForMissingEntities(entityName, entityKey) {
|
async function cleanupSyncRowsForMissingEntities(entityName, entityKey) {
|
||||||
@ -116,6 +110,5 @@ module.exports = {
|
|||||||
addAttributeSync,
|
addAttributeSync,
|
||||||
addApiTokenSync,
|
addApiTokenSync,
|
||||||
addEntitySync,
|
addEntitySync,
|
||||||
cleanupSyncRowsForMissingEntities,
|
|
||||||
fillAllSyncRows
|
fillAllSyncRows
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user