1
0
mirror of https://github.com/zadam/trilium.git synced 2025-03-01 14:22:32 +01:00

basic saving of attributes in the widget

This commit is contained in:
zadam 2020-06-05 17:25:14 +02:00
parent ad48b59893
commit f245d51746
8 changed files with 130 additions and 20 deletions
libraries/ckeditor
src
entities
public
routes

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -541,13 +541,14 @@ class Note extends Entity {
/**
* @return {Promise<Attribute>}
*/
async addAttribute(type, name, value = "", isInheritable = false) {
async addAttribute(type, name, value = "", isInheritable = false, position = 1000) {
const attr = new Attribute({
noteId: this.noteId,
type: type,
name: name,
value: value,
isInheritable: isInheritable
isInheritable: isInheritable,
position: position
});
await attr.save();

@ -1,9 +1,19 @@
function preprocessRelations(str) {
return str.replace(/<a[^>]+href="(#root[A-Za-z0-9/]*)"[^>]*>[^<]*<\/a>/g, "$1");
function preprocess(str) {
if (str.startsWith('<p>')) {
str = str.substr(3);
}
if (str.endsWith('</p>')) {
str = str.substr(0, str.length - 4);
}
str = str.replace("&nbsp;", " ");
return str.replace(/<a[^>]+href="(#[A-Za-z0-9/]*)"[^>]*>[^<]*<\/a>/g, "$1");
}
function lexer(str) {
str = preprocessRelations(str);
str = preprocess(str);
const expressionTokens = [];
@ -108,7 +118,8 @@ function parser(tokens) {
if (token.startsWith('#')) {
const attr = {
type: 'label',
name: token.substr(1)
name: token.substr(1),
isInheritable: false // FIXME
};
if (tokens[i + 1] === "=") {
@ -124,18 +135,25 @@ function parser(tokens) {
attrs.push(attr);
}
else if (token.startsWith('~')) {
const attr = {
type: 'relation',
name: token.substr(1)
};
if (i + 2 >= tokens.length || tokens[i + 1] !== '=') {
throw new Error(`Relation "${token}" should point to a note.`);
}
i += 2;
attr.value = tokens[i];
let notePath = tokens[i];
if (notePath.startsWith("#")) {
notePath = notePath.substr(1);
}
const noteId = notePath.split('/').pop();
const attr = {
type: 'relation',
name: token.substr(1),
isInheritable: false, // FIXME
value: noteId
};
attrs.push(attr);
}
@ -147,7 +165,14 @@ function parser(tokens) {
return attrs;
}
function lexAndParse(str) {
const tokens = lexer(str);
return parser(tokens);
}
export default {
lexer,
parser
parser,
lexAndParse
}

@ -6,7 +6,7 @@ import server from "../services/server.js";
import utils from "../services/utils.js";
import ws from "../services/ws.js";
import SpacedUpdate from "../services/spaced_update.js";
import protectedSessionHolder from "../services/protected_session_holder.js";
import attributesParser from "../services/attribute_parser.js";
const mentionSetup = {
feeds: [
@ -15,8 +15,6 @@ const mentionSetup = {
feed: queryText => {
return new Promise((res, rej) => {
noteAutocompleteService.autocompleteSource(queryText, rows => {
console.log("rows", rows);
res(rows.map(row => {
return {
id: '@' + row.notePathTitle,
@ -51,7 +49,8 @@ const mentionSetup = {
}
});
},
minimumCharacters: 0
minimumCharacters: 0,
attributeMention: true
},
{
marker: '~',
@ -65,7 +64,8 @@ const mentionSetup = {
}
});
},
minimumCharacters: 0
minimumCharacters: 0,
attributeMention: true
}
]
};
@ -92,6 +92,17 @@ export default class NoteAttributesWidget extends TabAwareWidget {
this.$editor = this.$widget.find('.note-attributes-editor');
this.initialized = this.initEditor();
this.$editor.keypress(async e => {
const keycode = (e.keyCode ? e.keyCode : e.which);
if (keycode === 13) {
const attributes = attributesParser.lexAndParse(this.textEditor.getData());
await server.put(`notes/${this.noteId}/attributes2`, attributes, this.componentId);
console.log("Saved!");
}
})
return this.$widget;
}

@ -800,3 +800,7 @@ body {
.hidden-int, .hidden-ext {
display: none !important;
}
.ck.ck-mentions > .ck-list__item {
max-width: 600px;
}

@ -1,6 +1,7 @@
"use strict";
const sql = require('../../services/sql');
const log = require('../../services/log');
const attributeService = require('../../services/attributes');
const repository = require('../../services/repository');
const Attribute = require('../../entities/attribute');
@ -82,6 +83,72 @@ async function deleteNoteAttribute(req) {
}
}
async function updateNoteAttributes2(req) {
const noteId = req.params.noteId;
const incomingAttributes = req.body;
const note = await repository.getNote(noteId);
let existingAttrs = await note.getAttributes();
let position = 0;
for (const incAttr of incomingAttributes) {
position += 10;
const perfectMatchAttr = existingAttrs.find(attr =>
attr.type === incAttr.type &&
attr.name === incAttr.name &&
attr.isInheritable === incAttr.isInheritable &&
attr.value === incAttr.value);
if (perfectMatchAttr) {
existingAttrs = existingAttrs.filter(attr => attr.attributeId !== perfectMatchAttr.attributeId);
if (perfectMatchAttr.position !== position) {
perfectMatchAttr.position = position;
await perfectMatchAttr.save();
}
continue; // nothing to update
}
if (incAttr.type === 'relation') {
const targetNote = await repository.getNote(incAttr.value);
if (!targetNote || targetNote.isDeleted) {
log.error(`Target note of relation ${JSON.stringify(incAttr)} does not exist or is deleted`);
continue;
}
}
const matchedAttr = existingAttrs.find(attr =>
attr.type === incAttr.type &&
attr.name === incAttr.name &&
attr.isInheritable === incAttr.isInheritable);
if (matchedAttr) {
matchedAttr.value = incAttr.value;
matchedAttr.position = position;
await matchedAttr.save();
existingAttrs = existingAttrs.filter(attr => attr.attributeId !== matchedAttr.attributeId);
continue;
}
// no existing attribute has been matched so we need to create a new one
// type, name and isInheritable are immutable so even if there is an attribute with matching type & name, we need to create a new one and delete the former one
await note.addAttribute(incAttr.type, incAttr.name, incAttr.value, incAttr.isInheritable, position);
}
// all the remaining existing attributes are not defined anymore and should be deleted
for (const toDeleteAttr of existingAttrs) {
toDeleteAttr.isDeleted = true;
await toDeleteAttr.save();
}
}
async function updateNoteAttributes(req) {
const noteId = req.params.noteId;
const attributes = req.body;
@ -193,6 +260,7 @@ async function deleteRelation(req) {
module.exports = {
updateNoteAttributes,
updateNoteAttributes2,
updateNoteAttribute,
deleteNoteAttribute,
getAttributeNames,

@ -167,6 +167,7 @@ function register(app) {
apiRoute(GET, '/api/notes/:noteId/attributes', attributesRoute.getEffectiveNoteAttributes);
apiRoute(PUT, '/api/notes/:noteId/attributes', attributesRoute.updateNoteAttributes);
apiRoute(PUT, '/api/notes/:noteId/attributes2', attributesRoute.updateNoteAttributes2);
apiRoute(PUT, '/api/notes/:noteId/attribute', attributesRoute.updateNoteAttribute);
apiRoute(PUT, '/api/notes/:noteId/relations/:name/to/:targetNoteId', attributesRoute.createRelation);
apiRoute(DELETE, '/api/notes/:noteId/relations/:name/to/:targetNoteId', attributesRoute.deleteRelation);