mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
cleanup of labels and relations from backend
This commit is contained in:
parent
3491235533
commit
1c0fd243d1
@ -65,30 +65,70 @@ class Note extends Entity {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLabels() {
|
async getOwnedAttributes() {
|
||||||
return await repository.getEntities("SELECT * FROM labels WHERE noteId = ? AND isDeleted = 0", [this.noteId]);
|
return await repository.getEntities(`SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ?`, [this.noteId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: this doesn't take into account the possibility to have multi-valued labels!
|
async getAttributes() {
|
||||||
async getLabelMap() {
|
const attributes = await repository.getEntities(`
|
||||||
const map = {};
|
WITH RECURSIVE tree(noteId, level) AS (
|
||||||
|
SELECT ?, 0
|
||||||
|
UNION
|
||||||
|
SELECT branches.parentNoteId, tree.level + 1 FROM branches
|
||||||
|
JOIN tree ON branches.noteId = tree.noteId
|
||||||
|
JOIN notes ON notes.noteId = branches.parentNoteId
|
||||||
|
WHERE notes.isDeleted = 0 AND branches.isDeleted = 0
|
||||||
|
)
|
||||||
|
SELECT attributes.* FROM attributes JOIN tree ON attributes.noteId = tree.noteId
|
||||||
|
WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR attributes.noteId = ?)
|
||||||
|
ORDER BY level, noteId, position`, [this.noteId, this.noteId]);
|
||||||
|
// attributes are ordered so that "closest" attributes are first
|
||||||
|
// we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter.
|
||||||
|
|
||||||
for (const label of await this.getLabels()) {
|
const filteredAttributes = attributes.filter((attr, index) => {
|
||||||
map[label.name] = label.value;
|
if (attr.isDefinition()) {
|
||||||
|
const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name);
|
||||||
|
|
||||||
|
// keep only if this element is the first definition for this type & name
|
||||||
|
return firstDefinitionIndex === index;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const definitionAttr = attributes.find(el => el.type === attr.type + '-definition' && el.name === attr.name);
|
||||||
|
|
||||||
|
if (!definitionAttr) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return map;
|
const definition = definitionAttr.value;
|
||||||
|
|
||||||
|
if (definition.multiplicityType === 'multivalue') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const firstAttrIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name);
|
||||||
|
|
||||||
|
// in case of single-valued attribute we'll keep it only if it's first (closest)
|
||||||
|
return firstAttrIndex === index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const attr of filteredAttributes) {
|
||||||
|
attr.isOwned = attr.noteId === this.noteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasLabel(name) {
|
async hasLabel(name) {
|
||||||
const map = await this.getLabelMap();
|
return !!await this.getLabel(name);
|
||||||
|
|
||||||
return map.hasOwnProperty(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 getLabel(name) {
|
||||||
return await repository.getEntity("SELECT * FROM labels WHERE noteId = ? AND name = ?", [this.noteId, name]);
|
const attributes = await this.getAttributes();
|
||||||
|
|
||||||
|
return attributes.find(attr => attr.type === 'label' && attr.name === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRevisions() {
|
async getRevisions() {
|
||||||
|
@ -6,7 +6,9 @@ const repository = require('../../services/repository');
|
|||||||
const Attribute = require('../../entities/attribute');
|
const Attribute = require('../../entities/attribute');
|
||||||
|
|
||||||
async function getEffectiveNoteAttributes(req) {
|
async function getEffectiveNoteAttributes(req) {
|
||||||
return await attributeService.getEffectiveAttributes(req.params.noteId);
|
const note = await repository.getNote(req.params.noteId);
|
||||||
|
|
||||||
|
return await note.getAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateNoteAttribute(req) {
|
async function updateNoteAttribute(req) {
|
||||||
@ -87,7 +89,9 @@ async function updateNoteAttributes(req) {
|
|||||||
await attributeEntity.save();
|
await attributeEntity.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return await attributeService.getEffectiveAttributes(noteId);
|
const note = await repository.getNote(noteId);
|
||||||
|
|
||||||
|
return await note.getAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAttributeNames(req) {
|
async function getAttributeNames(req) {
|
||||||
|
@ -113,15 +113,18 @@ async function exportToTar(branchId, res) {
|
|||||||
prefix: branch.prefix,
|
prefix: branch.prefix,
|
||||||
type: note.type,
|
type: note.type,
|
||||||
mime: note.mime,
|
mime: note.mime,
|
||||||
labels: (await note.getLabels()).map(label => {
|
attributes: (await note.getOwnedAttributes()).map(attribute => {
|
||||||
return {
|
return {
|
||||||
name: label.name,
|
type: attribute.type,
|
||||||
value: label.value
|
name: attribute.name,
|
||||||
|
value: attribute.value,
|
||||||
|
isInheritable: attribute.isInheritable,
|
||||||
|
position: attribute.position
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
if (metadata.labels.find(label => label.name === 'excludeFromExport')) {
|
if (metadata.attributes.find(attributes => attributes.type === 'label' && attributes.name === 'excludeFromExport')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const noteService = require('../../services/notes');
|
const noteService = require('../../services/notes');
|
||||||
const labelService = require('../../services/labels');
|
const attributeService = require('../../services/attributes');
|
||||||
const protectedSessionService = require('../../services/protected_session');
|
const protectedSessionService = require('../../services/protected_session');
|
||||||
const repository = require('../../services/repository');
|
const repository = require('../../services/repository');
|
||||||
|
|
||||||
@ -26,8 +26,8 @@ async function uploadFile(req) {
|
|||||||
mime: file.mimetype
|
mime: file.mimetype
|
||||||
});
|
});
|
||||||
|
|
||||||
await labelService.createLabel(note.noteId, "originalFileName", originalName);
|
await attributeService.createLabel(note.noteId, "originalFileName", originalName);
|
||||||
await labelService.createLabel(note.noteId, "fileSize", size);
|
await attributeService.createLabel(note.noteId, "fileSize", size);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
noteId: note.noteId
|
noteId: note.noteId
|
||||||
@ -47,8 +47,8 @@ async function downloadFile(req, res) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelMap = await note.getLabelMap();
|
const originalFileName = await note.getLabel('originalFileName');
|
||||||
const fileName = labelMap.originalFileName || note.title;
|
const fileName = originalFileName.value || note.title;
|
||||||
|
|
||||||
res.setHeader('Content-Disposition', 'file; filename="' + fileName + '"');
|
res.setHeader('Content-Disposition', 'file; filename="' + fileName + '"');
|
||||||
res.setHeader('Content-Type', note.mime);
|
res.setHeader('Content-Type', note.mime);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const repository = require('../../services/repository');
|
const repository = require('../../services/repository');
|
||||||
const labelService = require('../../services/labels');
|
const attributeService = require('../../services/attributes');
|
||||||
const noteService = require('../../services/notes');
|
const noteService = require('../../services/notes');
|
||||||
const Branch = require('../../entities/branch');
|
const Branch = require('../../entities/branch');
|
||||||
const tar = require('tar-stream');
|
const tar = require('tar-stream');
|
||||||
@ -187,8 +187,8 @@ async function importNotes(files, parentNoteId, noteIdMap) {
|
|||||||
|
|
||||||
noteIdMap[file.meta.noteId] = note.noteId;
|
noteIdMap[file.meta.noteId] = note.noteId;
|
||||||
|
|
||||||
for (const label of file.meta.labels) {
|
for (const attribute of file.meta.attributes) {
|
||||||
await labelService.createLabel(note.noteId, label.name, label.value);
|
await attributeService.createAttribute(attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.children.length > 0) {
|
if (file.children.length > 0) {
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const sql = require('../../services/sql');
|
|
||||||
const labelService = require('../../services/labels');
|
|
||||||
const repository = require('../../services/repository');
|
|
||||||
const Label = require('../../entities/label');
|
|
||||||
|
|
||||||
async function getNoteLabels(req) {
|
|
||||||
const noteId = req.params.noteId;
|
|
||||||
|
|
||||||
return await repository.getEntities("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateNoteLabels(req) {
|
|
||||||
const noteId = req.params.noteId;
|
|
||||||
const labels = req.body;
|
|
||||||
|
|
||||||
for (const label of labels) {
|
|
||||||
let labelEntity;
|
|
||||||
|
|
||||||
if (label.labelId) {
|
|
||||||
labelEntity = await repository.getLabel(label.labelId);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// if it was "created" and then immediatelly deleted, we just don't create it at all
|
|
||||||
if (label.isDeleted) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
labelEntity = new Label();
|
|
||||||
labelEntity.noteId = noteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
labelEntity.name = label.name;
|
|
||||||
labelEntity.value = label.value;
|
|
||||||
labelEntity.position = label.position;
|
|
||||||
labelEntity.isDeleted = label.isDeleted;
|
|
||||||
|
|
||||||
await labelEntity.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return await repository.getEntities("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getAllLabelNames() {
|
|
||||||
const names = await sql.getColumn("SELECT DISTINCT name FROM labels WHERE isDeleted = 0");
|
|
||||||
|
|
||||||
for (const label of labelService.BUILTIN_LABELS) {
|
|
||||||
if (!names.includes(label)) {
|
|
||||||
names.push(label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
names.sort();
|
|
||||||
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getValuesForLabel(req) {
|
|
||||||
const labelName = req.params.labelName;
|
|
||||||
|
|
||||||
return await sql.getColumn("SELECT DISTINCT value FROM labels WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [labelName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getNoteLabels,
|
|
||||||
updateNoteLabels,
|
|
||||||
getAllLabelNames,
|
|
||||||
getValuesForLabel
|
|
||||||
};
|
|
@ -1,64 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const sql = require('../../services/sql');
|
|
||||||
const relationService = require('../../services/relations');
|
|
||||||
const repository = require('../../services/repository');
|
|
||||||
const Relation = require('../../entities/relation');
|
|
||||||
|
|
||||||
async function getNoteRelations(req) {
|
|
||||||
const noteId = req.params.noteId;
|
|
||||||
|
|
||||||
return await repository.getEntities("SELECT * FROM relations WHERE isDeleted = 0 AND sourceNoteId = ? ORDER BY position, dateCreated", [noteId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateNoteRelations(req) {
|
|
||||||
const noteId = req.params.noteId;
|
|
||||||
const relations = req.body;
|
|
||||||
|
|
||||||
for (const relation of relations) {
|
|
||||||
let relationEntity;
|
|
||||||
|
|
||||||
if (relation.relationId) {
|
|
||||||
relationEntity = await repository.getRelation(relation.relationId);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// if it was "created" and then immediatelly deleted, we just don't create it at all
|
|
||||||
if (relation.isDeleted) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
relationEntity = new Relation();
|
|
||||||
relationEntity.sourceNoteId = noteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
relationEntity.name = relation.name;
|
|
||||||
relationEntity.targetNoteId = relation.targetNoteId;
|
|
||||||
relationEntity.isInheritable = relation.isInheritable;
|
|
||||||
relationEntity.position = relation.position;
|
|
||||||
relationEntity.isDeleted = relation.isDeleted;
|
|
||||||
|
|
||||||
await relationEntity.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return await repository.getEntities("SELECT * FROM relations WHERE isDeleted = 0 AND sourceNoteId = ? ORDER BY position, dateCreated", [noteId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getAllRelationNames() {
|
|
||||||
const names = await sql.getColumn("SELECT DISTINCT name FROM relations WHERE isDeleted = 0");
|
|
||||||
|
|
||||||
for (const relationName of relationService.BUILTIN_RELATIONS) {
|
|
||||||
if (!names.includes(relationName)) {
|
|
||||||
names.push(relationName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
names.sort();
|
|
||||||
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getNoteRelations,
|
|
||||||
updateNoteRelations,
|
|
||||||
getAllRelationNames
|
|
||||||
};
|
|
@ -1,6 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const labelService = require('../../services/labels');
|
|
||||||
const scriptService = require('../../services/script');
|
const scriptService = require('../../services/script');
|
||||||
const attributeService = require('../../services/attributes');
|
const attributeService = require('../../services/attributes');
|
||||||
const repository = require('../../services/repository');
|
const repository = require('../../services/repository');
|
||||||
@ -21,7 +20,7 @@ async function run(req) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getStartupBundles() {
|
async function getStartupBundles() {
|
||||||
const notes = await labelService.getNotesWithLabel("run", "frontendStartup");
|
const notes = await attributeService.getNotesWithLabel("run", "frontendStartup");
|
||||||
|
|
||||||
const bundles = [];
|
const bundles = [];
|
||||||
|
|
||||||
@ -38,9 +37,10 @@ async function getStartupBundles() {
|
|||||||
|
|
||||||
async function getRelationBundles(req) {
|
async function getRelationBundles(req) {
|
||||||
const noteId = req.params.noteId;
|
const noteId = req.params.noteId;
|
||||||
|
const note = await repository.getNote(noteId);
|
||||||
const relationName = req.params.relationName;
|
const relationName = req.params.relationName;
|
||||||
|
|
||||||
const attributes = await attributeService.getEffectiveAttributes(noteId);
|
const attributes = await note.getAttributes();
|
||||||
const filtered = attributes.filter(attr => attr.type === 'relation' && attr.name === relationName);
|
const filtered = attributes.filter(attr => attr.type === 'relation' && attr.name === relationName);
|
||||||
const targetNoteIds = filtered.map(relation => relation.value);
|
const targetNoteIds = filtered.map(relation => relation.value);
|
||||||
const uniqueNoteIds = Array.from(new Set(targetNoteIds));
|
const uniqueNoteIds = Array.from(new Set(targetNoteIds));
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const sourceIdService = require('../services/source_id');
|
const sourceIdService = require('../services/source_id');
|
||||||
const sql = require('../services/sql');
|
const sql = require('../services/sql');
|
||||||
const labelService = require('../services/labels');
|
const attributeService = require('../services/attributes');
|
||||||
const config = require('../services/config');
|
const config = require('../services/config');
|
||||||
const optionService = require('../services/options');
|
const optionService = require('../services/options');
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ async function index(req, res) {
|
|||||||
|
|
||||||
async function getAppCss() {
|
async function getAppCss() {
|
||||||
let css = '';
|
let css = '';
|
||||||
const notes = labelService.getNotesWithLabel('appCss');
|
const notes = attributeService.getNotesWithLabel('appCss');
|
||||||
|
|
||||||
for (const note of await notes) {
|
for (const note of await notes) {
|
||||||
css += `/* ${note.noteId} */
|
css += `/* ${note.noteId} */
|
||||||
|
@ -43,12 +43,17 @@ async function getNoteWithLabel(name, value) {
|
|||||||
return notes.length > 0 ? notes[0] : null;
|
return notes.length > 0 ? notes[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createAttribute(noteId, name, value = "") {
|
async function createLabel(noteId, name, value = "") {
|
||||||
return await new Attribute({
|
return await createAttribute({
|
||||||
noteId: noteId,
|
noteId: noteId,
|
||||||
|
type: 'label',
|
||||||
name: name,
|
name: name,
|
||||||
value: value
|
value: value
|
||||||
}).save();
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createAttribute(attribute) {
|
||||||
|
return await new Attribute(attribute).save();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAttributeNames(type, nameLike) {
|
async function getAttributeNames(type, nameLike) {
|
||||||
@ -70,62 +75,11 @@ async function getAttributeNames(type, nameLike) {
|
|||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getEffectiveAttributes(noteId) {
|
|
||||||
const attributes = await repository.getEntities(`
|
|
||||||
WITH RECURSIVE tree(noteId, level) AS (
|
|
||||||
SELECT ?, 0
|
|
||||||
UNION
|
|
||||||
SELECT branches.parentNoteId, tree.level + 1 FROM branches
|
|
||||||
JOIN tree ON branches.noteId = tree.noteId
|
|
||||||
JOIN notes ON notes.noteId = branches.parentNoteId
|
|
||||||
WHERE notes.isDeleted = 0 AND branches.isDeleted = 0
|
|
||||||
)
|
|
||||||
SELECT attributes.* FROM attributes JOIN tree ON attributes.noteId = tree.noteId
|
|
||||||
WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR attributes.noteId = ?)
|
|
||||||
ORDER BY level, noteId, position`, [noteId, noteId]);
|
|
||||||
// attributes are ordered so that "closest" attributes are first
|
|
||||||
// we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter.
|
|
||||||
|
|
||||||
const filteredAttributes = attributes.filter((attr, index) => {
|
|
||||||
if (attr.isDefinition()) {
|
|
||||||
const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name);
|
|
||||||
|
|
||||||
// keep only if this element is the first definition for this type & name
|
|
||||||
return firstDefinitionIndex === index;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const definitionAttr = attributes.find(el => el.type === attr.type + '-definition' && el.name === attr.name);
|
|
||||||
|
|
||||||
if (!definitionAttr) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const definition = definitionAttr.value;
|
|
||||||
|
|
||||||
if (definition.multiplicityType === 'multivalue') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const firstAttrIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name);
|
|
||||||
|
|
||||||
// in case of single-valued attribute we'll keep it only if it's first (closest)
|
|
||||||
return firstAttrIndex === index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const attr of filteredAttributes) {
|
|
||||||
attr.isOwned = attr.noteId === noteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return filteredAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getNotesWithLabel,
|
getNotesWithLabel,
|
||||||
getNoteWithLabel,
|
getNoteWithLabel,
|
||||||
|
createLabel,
|
||||||
createAttribute,
|
createAttribute,
|
||||||
getAttributeNames,
|
getAttributeNames,
|
||||||
getEffectiveAttributes,
|
|
||||||
BUILTIN_ATTRIBUTES
|
BUILTIN_ATTRIBUTES
|
||||||
};
|
};
|
@ -1,8 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const sql = require('./sql');
|
|
||||||
const noteService = require('./notes');
|
const noteService = require('./notes');
|
||||||
const labelService = require('./labels');
|
const attributeService = require('./attributes');
|
||||||
const dateUtils = require('./date_utils');
|
const dateUtils = require('./date_utils');
|
||||||
const repository = require('./repository');
|
const repository = require('./repository');
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ async function getNoteStartingWith(parentNoteId, startsWith) {
|
|||||||
|
|
||||||
async function getRootCalendarNote() {
|
async function getRootCalendarNote() {
|
||||||
// some caching here could be useful (e.g. in CLS)
|
// some caching here could be useful (e.g. in CLS)
|
||||||
let rootNote = await labelService.getNoteWithLabel(CALENDAR_ROOT_LABEL);
|
let rootNote = await attributeService.getNoteWithLabel(CALENDAR_ROOT_LABEL);
|
||||||
|
|
||||||
if (!rootNote) {
|
if (!rootNote) {
|
||||||
rootNote = (await noteService.createNewNote('root', {
|
rootNote = (await noteService.createNewNote('root', {
|
||||||
@ -41,8 +40,8 @@ async function getRootCalendarNote() {
|
|||||||
isProtected: false
|
isProtected: false
|
||||||
})).note;
|
})).note;
|
||||||
|
|
||||||
await labelService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL);
|
await attributeService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL);
|
||||||
await labelService.createLabel(rootNote.noteId, 'sorted');
|
await attributeService.createLabel(rootNote.noteId, 'sorted');
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootNote;
|
return rootNote;
|
||||||
@ -51,7 +50,7 @@ async function getRootCalendarNote() {
|
|||||||
async function getYearNote(dateTimeStr, rootNote) {
|
async function getYearNote(dateTimeStr, rootNote) {
|
||||||
const yearStr = dateTimeStr.substr(0, 4);
|
const yearStr = dateTimeStr.substr(0, 4);
|
||||||
|
|
||||||
let yearNote = await labelService.getNoteWithLabel(YEAR_LABEL, yearStr);
|
let yearNote = await attributeService.getNoteWithLabel(YEAR_LABEL, yearStr);
|
||||||
|
|
||||||
if (!yearNote) {
|
if (!yearNote) {
|
||||||
yearNote = await getNoteStartingWith(rootNote.noteId, yearStr);
|
yearNote = await getNoteStartingWith(rootNote.noteId, yearStr);
|
||||||
@ -60,8 +59,8 @@ async function getYearNote(dateTimeStr, rootNote) {
|
|||||||
yearNote = await createNote(rootNote.noteId, yearStr);
|
yearNote = await createNote(rootNote.noteId, yearStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
await labelService.createLabel(yearNote.noteId, YEAR_LABEL, yearStr);
|
await attributeService.createLabel(yearNote.noteId, YEAR_LABEL, yearStr);
|
||||||
await labelService.createLabel(yearNote.noteId, 'sorted');
|
await attributeService.createLabel(yearNote.noteId, 'sorted');
|
||||||
}
|
}
|
||||||
|
|
||||||
return yearNote;
|
return yearNote;
|
||||||
@ -71,7 +70,7 @@ async function getMonthNote(dateTimeStr, rootNote) {
|
|||||||
const monthStr = dateTimeStr.substr(0, 7);
|
const monthStr = dateTimeStr.substr(0, 7);
|
||||||
const monthNumber = dateTimeStr.substr(5, 2);
|
const monthNumber = dateTimeStr.substr(5, 2);
|
||||||
|
|
||||||
let monthNote = await labelService.getNoteWithLabel(MONTH_LABEL, monthStr);
|
let monthNote = await attributeService.getNoteWithLabel(MONTH_LABEL, monthStr);
|
||||||
|
|
||||||
if (!monthNote) {
|
if (!monthNote) {
|
||||||
const yearNote = await getYearNote(dateTimeStr, rootNote);
|
const yearNote = await getYearNote(dateTimeStr, rootNote);
|
||||||
@ -86,8 +85,8 @@ async function getMonthNote(dateTimeStr, rootNote) {
|
|||||||
monthNote = await createNote(yearNote.noteId, noteTitle);
|
monthNote = await createNote(yearNote.noteId, noteTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
await labelService.createLabel(monthNote.noteId, MONTH_LABEL, monthStr);
|
await attributeService.createLabel(monthNote.noteId, MONTH_LABEL, monthStr);
|
||||||
await labelService.createLabel(monthNote.noteId, 'sorted');
|
await attributeService.createLabel(monthNote.noteId, 'sorted');
|
||||||
}
|
}
|
||||||
|
|
||||||
return monthNote;
|
return monthNote;
|
||||||
@ -99,7 +98,7 @@ async function getDateNote(dateTimeStr) {
|
|||||||
const dateStr = dateTimeStr.substr(0, 10);
|
const dateStr = dateTimeStr.substr(0, 10);
|
||||||
const dayNumber = dateTimeStr.substr(8, 2);
|
const dayNumber = dateTimeStr.substr(8, 2);
|
||||||
|
|
||||||
let dateNote = await labelService.getNoteWithLabel(DATE_LABEL, dateStr);
|
let dateNote = await attributeService.getNoteWithLabel(DATE_LABEL, dateStr);
|
||||||
|
|
||||||
if (!dateNote) {
|
if (!dateNote) {
|
||||||
const monthNote = await getMonthNote(dateTimeStr, rootNote);
|
const monthNote = await getMonthNote(dateTimeStr, rootNote);
|
||||||
@ -114,7 +113,7 @@ async function getDateNote(dateTimeStr) {
|
|||||||
dateNote = await createNote(monthNote.noteId, noteTitle);
|
dateNote = await createNote(monthNote.noteId, noteTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
await labelService.createLabel(dateNote.noteId, DATE_LABEL, dateStr);
|
await attributeService.createLabel(dateNote.noteId, DATE_LABEL, dateStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dateNote;
|
return dateNote;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
const eventService = require('./events');
|
const eventService = require('./events');
|
||||||
const scriptService = require('./script');
|
const scriptService = require('./script');
|
||||||
const relationService = require('./relations');
|
|
||||||
const treeService = require('./tree');
|
const treeService = require('./tree');
|
||||||
const messagingService = require('./messaging');
|
const messagingService = require('./messaging');
|
||||||
|
|
||||||
eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => {
|
eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => {
|
||||||
const relations = await relationService.getEffectiveRelations(note.noteId, 'runOnNoteTitleChange');
|
const attributes = await note.getAttributes();
|
||||||
|
const runRelations = attributes.filter(relation => relation.type === 'relation' && relation.name === 'runOnNoteTitleChange');
|
||||||
|
|
||||||
for (const relation of relations) {
|
for (const relation of runRelations) {
|
||||||
const scriptNote = await relation.getTargetNote();
|
const scriptNote = await relation.getTargetNote();
|
||||||
|
|
||||||
await scriptService.executeNote(scriptNote, scriptNote, note);
|
await scriptService.executeNote(scriptNote, scriptNote, note);
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const repository = require('./repository');
|
|
||||||
const Label = require('../entities/label');
|
|
||||||
|
|
||||||
const BUILTIN_LABELS = [
|
|
||||||
'disableVersioning',
|
|
||||||
'calendarRoot',
|
|
||||||
'archived',
|
|
||||||
'excludeFromExport',
|
|
||||||
'run',
|
|
||||||
'manualTransactionHandling',
|
|
||||||
'disableInclusion',
|
|
||||||
'appCss',
|
|
||||||
'hideChildrenOverview'
|
|
||||||
];
|
|
||||||
|
|
||||||
async function getNotesWithLabel(name, value) {
|
|
||||||
let notes;
|
|
||||||
|
|
||||||
if (value !== undefined) {
|
|
||||||
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN labels USING(noteId)
|
|
||||||
WHERE notes.isDeleted = 0 AND labels.isDeleted = 0 AND labels.name = ? AND labels.value = ?`, [name, value]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN labels USING(noteId)
|
|
||||||
WHERE notes.isDeleted = 0 AND labels.isDeleted = 0 AND labels.name = ?`, [name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return notes;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getNoteWithLabel(name, value) {
|
|
||||||
const notes = await getNotesWithLabel(name, value);
|
|
||||||
|
|
||||||
return notes.length > 0 ? notes[0] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createLabel(noteId, name, value = "") {
|
|
||||||
return await new Label({
|
|
||||||
noteId: noteId,
|
|
||||||
name: name,
|
|
||||||
value: value
|
|
||||||
}).save();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getNotesWithLabel,
|
|
||||||
getNoteWithLabel,
|
|
||||||
createLabel,
|
|
||||||
BUILTIN_LABELS
|
|
||||||
};
|
|
@ -2,7 +2,7 @@ const sql = require('./sql');
|
|||||||
const optionService = require('./options');
|
const optionService = require('./options');
|
||||||
const dateUtils = require('./date_utils');
|
const dateUtils = require('./date_utils');
|
||||||
const syncTableService = require('./sync_table');
|
const syncTableService = require('./sync_table');
|
||||||
const labelService = require('./labels');
|
const attributeService = require('./attributes');
|
||||||
const eventService = require('./events');
|
const eventService = require('./events');
|
||||||
const repository = require('./repository');
|
const repository = require('./repository');
|
||||||
const Note = require('../entities/note');
|
const Note = require('../entities/note');
|
||||||
@ -93,9 +93,10 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
|
|||||||
|
|
||||||
const {note, branch} = await createNewNote(parentNoteId, noteData);
|
const {note, branch} = await createNewNote(parentNoteId, noteData);
|
||||||
|
|
||||||
|
// FIXME: need to make this more generic for all kinds of attributes
|
||||||
if (extraOptions.labels) {
|
if (extraOptions.labels) {
|
||||||
for (const labelName in extraOptions.labels) {
|
for (const labelName in extraOptions.labels) {
|
||||||
await labelService.createLabel(note.noteId, labelName, extraOptions.labels[labelName]);
|
await attributeService.createLabel(note.noteId, labelName, extraOptions.labels[labelName]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,8 +169,6 @@ async function saveNoteImages(note) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveNoteRevision(note) {
|
async function saveNoteRevision(note) {
|
||||||
const labelsMap = await note.getLabelMap();
|
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const noteRevisionSnapshotTimeInterval = parseInt(await optionService.getOption('noteRevisionSnapshotTimeInterval'));
|
const noteRevisionSnapshotTimeInterval = parseInt(await optionService.getOption('noteRevisionSnapshotTimeInterval'));
|
||||||
|
|
||||||
@ -181,7 +180,7 @@ async function saveNoteRevision(note) {
|
|||||||
const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.dateCreated).getTime();
|
const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.dateCreated).getTime();
|
||||||
|
|
||||||
if (note.type !== 'file'
|
if (note.type !== 'file'
|
||||||
&& labelsMap.disableVersioning !== 'true'
|
&& await note.hasLabel('disableVersioning')
|
||||||
&& !existingnoteRevisionId
|
&& !existingnoteRevisionId
|
||||||
&& msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) {
|
&& msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) {
|
||||||
|
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const repository = require('./repository');
|
|
||||||
const Relation = require('../entities/relation');
|
|
||||||
|
|
||||||
const BUILTIN_RELATIONS = [
|
|
||||||
'runOnNoteView',
|
|
||||||
'runOnNoteTitleChange'
|
|
||||||
];
|
|
||||||
|
|
||||||
async function getNotesWithRelation(name, targetNoteId) {
|
|
||||||
let notes;
|
|
||||||
|
|
||||||
if (targetNoteId !== undefined) {
|
|
||||||
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN relations ON notes.noteId = relations.sourceNoteId
|
|
||||||
WHERE notes.isDeleted = 0 AND relations.isDeleted = 0 AND relations.name = ? AND relations.targetNoteId = ?`, [name, targetNoteId]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN relations ON notes.noteId = relations.sourceNoteId
|
|
||||||
WHERE notes.isDeleted = 0 AND relations.isDeleted = 0 AND relations.name = ?`, [name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return notes;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getNoteWithRelation(name, value) {
|
|
||||||
const notes = await getNotesWithRelation(name, value);
|
|
||||||
|
|
||||||
return notes.length > 0 ? notes[0] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createRelation(sourceNoteId, name, targetNoteId) {
|
|
||||||
return await new Relation({
|
|
||||||
sourceNoteId: sourceNoteId,
|
|
||||||
name: name,
|
|
||||||
targetNoteId: targetNoteId
|
|
||||||
}).save();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getEffectiveRelations(noteId, relationName) {
|
|
||||||
const relations = await repository.getEntities(`
|
|
||||||
WITH RECURSIVE tree(noteId) AS (
|
|
||||||
SELECT ?
|
|
||||||
UNION
|
|
||||||
SELECT branches.parentNoteId FROM branches
|
|
||||||
JOIN tree ON branches.noteId = tree.noteId
|
|
||||||
JOIN notes ON notes.noteId = branches.parentNoteId
|
|
||||||
WHERE notes.isDeleted = 0 AND branches.isDeleted = 0
|
|
||||||
)
|
|
||||||
SELECT relations.* FROM relations JOIN tree ON relations.sourceNoteId = tree.noteId
|
|
||||||
WHERE relations.isDeleted = 0 AND (relations.isInheritable = 1 OR relations.sourceNoteId = ?)`, [noteId, noteId]);
|
|
||||||
|
|
||||||
if (relationName) {
|
|
||||||
return relations.filter(relation => relation.name === relationName);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return relations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
BUILTIN_RELATIONS,
|
|
||||||
getNotesWithRelation,
|
|
||||||
getNoteWithRelation,
|
|
||||||
createRelation,
|
|
||||||
getEffectiveRelations
|
|
||||||
};
|
|
@ -41,14 +41,6 @@ async function getAttribute(attributeId) {
|
|||||||
return await getEntity("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]);
|
return await getEntity("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLabel(labelId) {
|
|
||||||
return await getEntity("SELECT * FROM labels WHERE labelId = ?", [labelId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getRelation(relationId) {
|
|
||||||
return await getEntity("SELECT * FROM relations WHERE relationId = ?", [relationId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getOption(name) {
|
async function getOption(name) {
|
||||||
return await getEntity("SELECT * FROM options WHERE name = ?", [name]);
|
return await getEntity("SELECT * FROM options WHERE name = ?", [name]);
|
||||||
}
|
}
|
||||||
@ -61,6 +53,7 @@ async function updateEntity(entity) {
|
|||||||
const clone = Object.assign({}, entity);
|
const clone = Object.assign({}, entity);
|
||||||
|
|
||||||
delete clone.jsonContent;
|
delete clone.jsonContent;
|
||||||
|
delete clone.isOwned;
|
||||||
|
|
||||||
for (const key in clone) {
|
for (const key in clone) {
|
||||||
if (clone[key] !== null && typeof clone[key] === 'object') {
|
if (clone[key] !== null && typeof clone[key] === 'object') {
|
||||||
@ -86,8 +79,6 @@ module.exports = {
|
|||||||
getBranch,
|
getBranch,
|
||||||
getImage,
|
getImage,
|
||||||
getAttribute,
|
getAttribute,
|
||||||
getLabel,
|
|
||||||
getRelation,
|
|
||||||
getOption,
|
getOption,
|
||||||
updateEntity,
|
updateEntity,
|
||||||
setEntityConstructor
|
setEntityConstructor
|
||||||
|
@ -235,8 +235,6 @@ const primaryKeys = {
|
|||||||
"recent_notes": "branchId",
|
"recent_notes": "branchId",
|
||||||
"images": "imageId",
|
"images": "imageId",
|
||||||
"note_images": "noteImageId",
|
"note_images": "noteImageId",
|
||||||
"labels": "labelId",
|
|
||||||
"relations": "relationId",
|
|
||||||
"api_tokens": "apiTokenId",
|
"api_tokens": "apiTokenId",
|
||||||
"options": "name"
|
"options": "name"
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const sql = require('./sql');
|
const sql = require('./sql');
|
||||||
const sourceIdService = require('./source_id');
|
const sourceIdService = require('./source_id');
|
||||||
const dateUtils = require('./date_utils');
|
const dateUtils = require('./date_utils');
|
||||||
const syncOptions = require('./sync_options');
|
|
||||||
const log = require('./log');
|
const log = require('./log');
|
||||||
const cls = require('./cls');
|
const cls = require('./cls');
|
||||||
const eventService = require('./events');
|
const eventService = require('./events');
|
||||||
@ -42,14 +41,6 @@ async function addAttributeSync(attributeId, sourceId) {
|
|||||||
await addEntitySync("attributes", attributeId, sourceId);
|
await addEntitySync("attributes", attributeId, sourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addLabelSync(labelId, sourceId) {
|
|
||||||
await addEntitySync("labels", labelId, sourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addRelationSync(relationId, sourceId) {
|
|
||||||
await addEntitySync("relations", relationId, sourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addApiTokenSync(apiTokenId, sourceId) {
|
async function addApiTokenSync(apiTokenId, sourceId) {
|
||||||
await addEntitySync("api_tokens", apiTokenId, sourceId);
|
await addEntitySync("api_tokens", apiTokenId, sourceId);
|
||||||
}
|
}
|
||||||
@ -109,8 +100,6 @@ async function fillAllSyncRows() {
|
|||||||
await fillSyncRows("images", "imageId");
|
await fillSyncRows("images", "imageId");
|
||||||
await fillSyncRows("note_images", "noteImageId");
|
await fillSyncRows("note_images", "noteImageId");
|
||||||
await fillSyncRows("attributes", "attributeId");
|
await fillSyncRows("attributes", "attributeId");
|
||||||
await fillSyncRows("labels", "labelId");
|
|
||||||
await fillSyncRows("relations", "relationId");
|
|
||||||
await fillSyncRows("api_tokens", "apiTokenId");
|
await fillSyncRows("api_tokens", "apiTokenId");
|
||||||
await fillSyncRows("options", "name", 'isSynced = 1');
|
await fillSyncRows("options", "name", 'isSynced = 1');
|
||||||
}
|
}
|
||||||
@ -125,8 +114,6 @@ module.exports = {
|
|||||||
addImageSync,
|
addImageSync,
|
||||||
addNoteImageSync,
|
addNoteImageSync,
|
||||||
addAttributeSync,
|
addAttributeSync,
|
||||||
addLabelSync,
|
|
||||||
addRelationSync,
|
|
||||||
addApiTokenSync,
|
addApiTokenSync,
|
||||||
addEntitySync,
|
addEntitySync,
|
||||||
cleanupSyncRowsForMissingEntities,
|
cleanupSyncRowsForMissingEntities,
|
||||||
|
@ -33,12 +33,6 @@ async function updateEntity(sync, entity, sourceId) {
|
|||||||
else if (entityName === 'attributes') {
|
else if (entityName === 'attributes') {
|
||||||
await updateAttribute(entity, sourceId);
|
await updateAttribute(entity, sourceId);
|
||||||
}
|
}
|
||||||
else if (entityName === 'labels') {
|
|
||||||
await updateLabel(entity, sourceId);
|
|
||||||
}
|
|
||||||
else if (entityName === 'relations') {
|
|
||||||
await updateRelation(entity, sourceId);
|
|
||||||
}
|
|
||||||
else if (entityName === 'api_tokens') {
|
else if (entityName === 'api_tokens') {
|
||||||
await updateApiToken(entity, sourceId);
|
await updateApiToken(entity, sourceId);
|
||||||
}
|
}
|
||||||
@ -191,34 +185,6 @@ async function updateAttribute(entity, sourceId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateLabel(entity, sourceId) {
|
|
||||||
const origLabel = await sql.getRow("SELECT * FROM labels WHERE labelId = ?", [entity.labelId]);
|
|
||||||
|
|
||||||
if (!origLabel || origLabel.dateModified <= entity.dateModified) {
|
|
||||||
await sql.transactional(async () => {
|
|
||||||
await sql.replace("labels", entity);
|
|
||||||
|
|
||||||
await syncTableService.addLabelSync(entity.labelId, sourceId);
|
|
||||||
});
|
|
||||||
|
|
||||||
log.info("Update/sync label " + entity.labelId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateRelation(entity, sourceId) {
|
|
||||||
const origRelation = await sql.getRow("SELECT * FROM relations WHERE relationId = ?", [entity.relationId]);
|
|
||||||
|
|
||||||
if (!origRelation || origRelation.dateModified <= entity.dateModified) {
|
|
||||||
await sql.transactional(async () => {
|
|
||||||
await sql.replace("relations", entity);
|
|
||||||
|
|
||||||
await syncTableService.addRelationSync(entity.relationId, sourceId);
|
|
||||||
});
|
|
||||||
|
|
||||||
log.info("Update/sync relation " + entity.relationId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateApiToken(entity, sourceId) {
|
async function updateApiToken(entity, sourceId) {
|
||||||
const apiTokenId = await sql.getRow("SELECT * FROM api_tokens WHERE apiTokenId = ?", [entity.apiTokenId]);
|
const apiTokenId = await sql.getRow("SELECT * FROM api_tokens WHERE apiTokenId = ?", [entity.apiTokenId]);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user