diff --git a/src/public/javascripts/dialogs/labels.js b/src/public/javascripts/dialogs/labels.js deleted file mode 100644 index 3a68ca71b..000000000 --- a/src/public/javascripts/dialogs/labels.js +++ /dev/null @@ -1,222 +0,0 @@ -import noteDetailService from '../services/note_detail.js'; -import server from '../services/server.js'; -import infoService from "../services/info.js"; - -const $dialog = $("#labels-dialog"); -const $saveLabelsButton = $("#save-labels-button"); -const $labelsBody = $('#labels-table tbody'); - -const labelsModel = new LabelsModel(); -let labelNames = []; - -function LabelsModel() { - const self = this; - - this.labels = ko.observableArray(); - - this.updateLabelPositions = function() { - let position = 0; - - // we need to update positions by searching in the DOM, because order of the - // labels in the viewmodel (self.labels()) stays the same - $labelsBody.find('input[name="position"]').each(function() { - const label = self.getTargetLabel(this); - - label().position = position++; - }); - }; - - this.loadLabels = async function() { - const noteId = noteDetailService.getCurrentNoteId(); - - const labels = await server.get('notes/' + noteId + '/labels'); - - self.labels(labels.map(ko.observable)); - - addLastEmptyRow(); - - labelNames = await server.get('labels/names'); - - // label might not be rendered immediatelly so could not focus - setTimeout(() => $(".label-name:last").focus(), 100); - - $labelsBody.sortable({ - handle: '.handle', - containment: $labelsBody, - update: this.updateLabelPositions - }); - }; - - this.deleteLabel = function(data, event) { - const label = self.getTargetLabel(event.target); - const labelData = label(); - - if (labelData) { - labelData.isDeleted = true; - - label(labelData); - - addLastEmptyRow(); - } - }; - - function isValid() { - for (let labels = self.labels(), i = 0; i < labels.length; i++) { - if (self.isEmptyName(i)) { - return false; - } - } - - return true; - } - - this.save = async function() { - // we need to defocus from input (in case of enter-triggered save) because value is updated - // on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would - // stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel. - $saveLabelsButton.focus(); - - if (!isValid()) { - alert("Please fix all validation errors and try saving again."); - return; - } - - self.updateLabelPositions(); - - const noteId = noteDetailService.getCurrentNoteId(); - - const labelsToSave = self.labels() - .map(label => label()) - .filter(label => label.labelId !== "" || label.name !== ""); - - const labels = await server.put('notes/' + noteId + '/labels', labelsToSave); - - self.labels(labels.map(ko.observable)); - - addLastEmptyRow(); - - infoService.showMessage("Labels have been saved."); - - noteDetailService.loadLabelList(); - }; - - function addLastEmptyRow() { - const labels = self.labels().filter(attr => !attr().isDeleted); - const last = labels.length === 0 ? null : labels[labels.length - 1](); - - if (!last || last.name.trim() !== "" || last.value !== "") { - self.labels.push(ko.observable({ - labelId: '', - name: '', - value: '', - isDeleted: false, - position: 0 - })); - } - } - - this.labelChanged = function (data, event) { - addLastEmptyRow(); - - const label = self.getTargetLabel(event.target); - - label.valueHasMutated(); - }; - - this.isNotUnique = function(index) { - const cur = self.labels()[index](); - - if (cur.name.trim() === "") { - return false; - } - - for (let labels = self.labels(), i = 0; i < labels.length; i++) { - const label = labels[i](); - - if (index !== i && cur.name === label.name) { - return true; - } - } - - return false; - }; - - this.isEmptyName = function(index) { - const cur = self.labels()[index](); - - return cur.name.trim() === "" && (cur.labelId !== "" || cur.value !== ""); - }; - - this.getTargetLabel = function(target) { - const context = ko.contextFor(target); - const index = context.$index(); - - return self.labels()[index]; - } -} - -async function showDialog() { - glob.activeDialog = $dialog; - - await labelsModel.loadLabels(); - - $dialog.dialog({ - modal: true, - width: 800, - height: 500 - }); -} - -ko.applyBindings(labelsModel, $dialog[0]); - -$dialog.on('focus', '.label-name', function (e) { - if (!$(this).hasClass("ui-autocomplete-input")) { - $(this).autocomplete({ - // shouldn't be required and autocomplete should just accept array of strings, but that fails - // because we have overriden filter() function in autocomplete.js - source: labelNames.map(label => { - return { - label: label, - value: label - } - }), - minLength: 0 - }); - } - - $(this).autocomplete("search", $(this).val()); -}); - -$dialog.on('focus', '.label-value', async function (e) { - if (!$(this).hasClass("ui-autocomplete-input")) { - const labelName = $(this).parent().parent().find('.label-name').val(); - - if (labelName.trim() === "") { - return; - } - - const labelValues = await server.get('labels/values/' + encodeURIComponent(labelName)); - - if (labelValues.length === 0) { - return; - } - - $(this).autocomplete({ - // shouldn't be required and autocomplete should just accept array of strings, but that fails - // because we have overriden filter() function in autocomplete.js - source: labelValues.map(label => { - return { - label: label, - value: label - } - }), - minLength: 0 - }); - } - - $(this).autocomplete("search", $(this).val()); -}); - -export default { - showDialog -}; \ No newline at end of file diff --git a/src/public/javascripts/dialogs/relations.js b/src/public/javascripts/dialogs/relations.js deleted file mode 100644 index 97bfad225..000000000 --- a/src/public/javascripts/dialogs/relations.js +++ /dev/null @@ -1,250 +0,0 @@ -import noteDetailService from '../services/note_detail.js'; -import server from '../services/server.js'; -import infoService from "../services/info.js"; -import linkService from "../services/link.js"; -import treeUtils from "../services/tree_utils.js"; - -const $dialog = $("#relations-dialog"); -const $saveRelationsButton = $("#save-relations-button"); -const $relationsBody = $('#relations-table tbody'); - -const relationsModel = new RelationsModel(); -let relationNames = []; - -function RelationsModel() { - const self = this; - - this.relations = ko.observableArray(); - - this.updateRelationPositions = function() { - let position = 0; - - // we need to update positions by searching in the DOM, because order of the - // relations in the viewmodel (self.relations()) stays the same - $relationsBody.find('input[name="position"]').each(function() { - const relation = self.getTargetRelation(this); - - relation().position = position++; - }); - }; - - async function showRelations(relations) { - for (const relation of relations) { - relation.targetNoteId = await treeUtils.getNoteTitle(relation.targetNoteId) + " (" + relation.targetNoteId + ")"; - } - - self.relations(relations.map(ko.observable)); - } - - this.loadRelations = async function() { - const noteId = noteDetailService.getCurrentNoteId(); - - const relations = await server.get('notes/' + noteId + '/relations'); - - await showRelations(relations); - - addLastEmptyRow(); - - relationNames = await server.get('relations/names'); - - // relation might not be rendered immediatelly so could not focus - setTimeout(() => $(".relation-name:last").focus(), 100); - - $relationsBody.sortable({ - handle: '.handle', - containment: $relationsBody, - update: this.updateRelationPositions - }); - }; - - this.deleteRelation = function(data, event) { - const relation = self.getTargetRelation(event.target); - const relationData = relation(); - - if (relationData) { - relationData.isDeleted = true; - - relation(relationData); - - addLastEmptyRow(); - } - }; - - function isValid() { - for (let relations = self.relations(), i = 0; i < relations.length; i++) { - if (self.isEmptyName(i)) { - return false; - } - } - - return true; - } - - this.save = async function() { - // we need to defocus from input (in case of enter-triggered save) because value is updated - // on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would - // stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel. - $saveRelationsButton.focus(); - - if (!isValid()) { - alert("Please fix all validation errors and try saving again."); - return; - } - - self.updateRelationPositions(); - - const noteId = noteDetailService.getCurrentNoteId(); - - const relationsToSave = self.relations() - .map(relation => relation()) - .filter(relation => relation.relationId !== "" || relation.name !== ""); - - relationsToSave.forEach(relation => relation.targetNoteId = treeUtils.getNoteIdFromNotePath(linkService.getNotePathFromLabel(relation.targetNoteId))); - - console.log(relationsToSave); - - const relations = await server.put('notes/' + noteId + '/relations', relationsToSave); - - await showRelations(relations); - - addLastEmptyRow(); - - infoService.showMessage("Relations have been saved."); - - noteDetailService.loadRelationList(); - }; - - function addLastEmptyRow() { - const relations = self.relations().filter(attr => !attr().isDeleted); - const last = relations.length === 0 ? null : relations[relations.length - 1](); - - if (!last || last.name.trim() !== "" || last.targetNoteId !== "") { - self.relations.push(ko.observable({ - relationId: '', - name: '', - targetNoteId: '', - isInheritable: false, - isDeleted: false, - position: 0 - })); - } - } - - this.relationChanged = function (data, event) { - addLastEmptyRow(); - - const relation = self.getTargetRelation(event.target); - - relation.valueHasMutated(); - }; - - this.isNotUnique = function(index) { - const cur = self.relations()[index](); - - if (cur.name.trim() === "") { - return false; - } - - for (let relations = self.relations(), i = 0; i < relations.length; i++) { - const relation = relations[i](); - - if (index !== i && cur.name === relation.name) { - return true; - } - } - - return false; - }; - - this.isEmptyName = function(index) { - const cur = self.relations()[index](); - - return cur.name.trim() === "" && (cur.relationId !== "" || cur.targetNoteId !== ""); - }; - - this.getTargetRelation = function(target) { - const context = ko.contextFor(target); - const index = context.$index(); - - return self.relations()[index]; - } -} - -async function showDialog() { - glob.activeDialog = $dialog; - - await relationsModel.loadRelations(); - - $dialog.dialog({ - modal: true, - width: 900, - height: 500 - }); -} - -ko.applyBindings(relationsModel, document.getElementById('relations-dialog')); - -$dialog.on('focus', '.relation-name', function (e) { - if (!$(this).hasClass("ui-autocomplete-input")) { - $(this).autocomplete({ - // shouldn't be required and autocomplete should just accept array of strings, but that fails - // because we have overriden filter() function in autocomplete.js - source: relationNames.map(relation => { - return { - label: relation, - value: relation - } - }), - minLength: 0 - }); - } - - $(this).autocomplete("search", $(this).val()); -}); - -async function initNoteAutocomplete($el) { - if (!$el.hasClass("ui-autocomplete-input")) { - await $el.autocomplete({ - source: async function (request, response) { - const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term)); - - if (result.length > 0) { - response(result.map(row => { - return { - label: row.label, - value: row.label + ' (' + row.value + ')' - } - })); - } - else { - response([{ - label: "No results", - value: "No results" - }]); - } - }, - minLength: 0, - select: function (event, ui) { - if (ui.item.value === 'No results') { - return false; - } - } - }); - } -} - -$dialog.on('focus', '.relation-target-note-id', async function () { - await initNoteAutocomplete($(this)); -}); - -$dialog.on('click', '.relations-show-recent-notes', async function () { - const $autocomplete = $(this).parent().find('.relation-target-note-id'); - - await initNoteAutocomplete($autocomplete); - - $autocomplete.autocomplete("search", ""); -}); - -export default { - showDialog -}; \ No newline at end of file diff --git a/src/public/javascripts/services/bootstrap.js b/src/public/javascripts/services/bootstrap.js index 15236b222..23c09a024 100644 --- a/src/public/javascripts/services/bootstrap.js +++ b/src/public/javascripts/services/bootstrap.js @@ -1,6 +1,5 @@ import addLinkDialog from '../dialogs/add_link.js'; import jumpToNoteDialog from '../dialogs/jump_to_note.js'; -import labelsDialog from '../dialogs/labels.js'; import attributesDialog from '../dialogs/attributes.js'; import noteRevisionsDialog from '../dialogs/note_revisions.js'; import noteSourceDialog from '../dialogs/note_source.js'; diff --git a/src/public/javascripts/services/entrypoints.js b/src/public/javascripts/services/entrypoints.js index 0d11d92ae..c1aa67c05 100644 --- a/src/public/javascripts/services/entrypoints.js +++ b/src/public/javascripts/services/entrypoints.js @@ -12,8 +12,6 @@ import recentChangesDialog from "../dialogs/recent_changes.js"; import sqlConsoleDialog from "../dialogs/sql_console.js"; import searchNotesService from "./search_notes.js"; import attributesDialog from "../dialogs/attributes.js"; -import labelsDialog from "../dialogs/labels.js"; -import relationsDialog from "../dialogs/relations.js"; import protectedSessionService from "./protected_session.js"; function registerEntrypoints() { @@ -42,12 +40,6 @@ function registerEntrypoints() { $(".show-attributes-button").click(attributesDialog.showDialog); utils.bindShortcut('alt+a', attributesDialog.showDialog); - $(".show-labels-button").click(labelsDialog.showDialog); - utils.bindShortcut('alt+l', labelsDialog.showDialog); - - $(".show-relations-button").click(relationsDialog.showDialog); - utils.bindShortcut('alt+r', relationsDialog.showDialog); - $("#options-button").click(optionsDialog.showDialog); utils.bindShortcut('alt+o', sqlConsoleDialog.showDialog); diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index e4ab7256a..862f0cf9d 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -29,10 +29,6 @@ const $noteDetailComponentWrapper = $("#note-detail-component-wrapper"); const $noteIdDisplay = $("#note-id-display"); const $attributeList = $("#attribute-list"); const $attributeListInner = $("#attribute-list-inner"); -const $labelList = $("#label-list"); -const $labelListInner = $("#label-list-inner"); -const $relationList = $("#relation-list"); -const $relationListInner = $("#relation-list-inner"); const $childrenOverview = $("#children-overview"); const $scriptArea = $("#note-detail-script-area"); const $promotedAttributesContainer = $("#note-detail-promoted-attributes"); @@ -187,18 +183,14 @@ async function loadNoteDetail(noteId) { // after loading new note make sure editor is scrolled to the top $noteDetailWrapper.scrollTop(0); - const labels = await loadLabelList(); - - const hideChildrenOverview = labels.some(label => label.name === 'hideChildrenOverview'); - await showChildrenOverview(hideChildrenOverview); - - await loadRelationList(); - $scriptArea.html(''); await bundleService.executeRelationBundles(getCurrentNote(), 'runOnNoteView'); - await loadAttributes(); + const attributes = await loadAttributes(); + + const hideChildrenOverview = attributes.some(attr => attr.type === 'label' && attr.name === 'hideChildrenOverview'); + await showChildrenOverview(hideChildrenOverview); } async function showChildrenOverview(hideChildrenOverview) { @@ -411,50 +403,8 @@ async function loadAttributes() { $attributeList.show(); } } -} -async function loadLabelList() { - const noteId = getCurrentNoteId(); - - const labels = await server.get('notes/' + noteId + '/labels'); - - $labelListInner.html(''); - - if (labels.length > 0) { - for (const label of labels) { - $labelListInner.append(utils.formatLabel(label) + " "); - } - - $labelList.show(); - } - else { - $labelList.hide(); - } - - return labels; -} - -async function loadRelationList() { - const noteId = getCurrentNoteId(); - - const relations = await server.get('notes/' + noteId + '/relations'); - - $relationListInner.html(''); - - if (relations.length > 0) { - for (const relation of relations) { - $relationListInner.append(relation.name + " = "); - $relationListInner.append(await linkService.createNoteLink(relation.targetNoteId)); - $relationListInner.append(" "); - } - - $relationList.show(); - } - else { - $relationList.hide(); - } - - return relations; + return attributes; } async function loadNote(noteId) { @@ -535,8 +485,6 @@ export default { newNoteCreated, focus, loadAttributes, - loadLabelList, - loadRelationList, saveNote, saveNoteIfChanged, noteChanged diff --git a/src/routes/api/attributes.js b/src/routes/api/attributes.js index 2af04929f..df25c8572 100644 --- a/src/routes/api/attributes.js +++ b/src/routes/api/attributes.js @@ -6,56 +6,7 @@ const repository = require('../../services/repository'); const Attribute = require('../../entities/attribute'); async function getEffectiveNoteAttributes(req) { - const noteId = req.params.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; + return await attributeService.getEffectiveAttributes(req.params.noteId); } async function updateNoteAttribute(req) { @@ -136,7 +87,7 @@ async function updateNoteAttributes(req) { await attributeEntity.save(); } - return await getEffectiveNoteAttributes(req); + return await attributeService.getEffectiveAttributes(noteId); } async function getAttributeNames(req) { diff --git a/src/routes/api/script.js b/src/routes/api/script.js index d5e648397..b1d93c4cb 100644 --- a/src/routes/api/script.js +++ b/src/routes/api/script.js @@ -2,7 +2,7 @@ const labelService = require('../../services/labels'); const scriptService = require('../../services/script'); -const relationService = require('../../services/relations'); +const attributeService = require('../../services/attributes'); const repository = require('../../services/repository'); async function exec(req) { @@ -40,9 +40,9 @@ async function getRelationBundles(req) { const noteId = req.params.noteId; const relationName = req.params.relationName; - const relations = await relationService.getEffectiveRelations(noteId); - const filtered = relations.filter(relation => relation.name === relationName); - const targetNoteIds = filtered.map(relation => relation.targetNoteId); + const attributes = await attributeService.getEffectiveAttributes(noteId); + const filtered = attributes.filter(attr => attr.type === 'relation' && attr.name === relationName); + const targetNoteIds = filtered.map(relation => relation.value); const uniqueNoteIds = Array.from(new Set(targetNoteIds)); const bundles = []; diff --git a/src/routes/routes.js b/src/routes/routes.js index 51729f283..21f5bfb0f 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -26,8 +26,6 @@ const anonymizationRoute = require('./api/anonymization'); const cleanupRoute = require('./api/cleanup'); const imageRoute = require('./api/image'); const attributesRoute = require('./api/attributes'); -const labelsRoute = require('./api/labels'); -const relationsRoute = require('./api/relations'); const scriptRoute = require('./api/script'); const senderRoute = require('./api/sender'); const filesRoute = require('./api/file_upload'); @@ -141,15 +139,6 @@ function register(app) { apiRoute(GET, '/api/attributes/names', attributesRoute.getAttributeNames); apiRoute(GET, '/api/attributes/values/:attributeName', attributesRoute.getValuesForAttribute); - apiRoute(GET, '/api/notes/:noteId/labels', labelsRoute.getNoteLabels); - apiRoute(PUT, '/api/notes/:noteId/labels', labelsRoute.updateNoteLabels); - apiRoute(GET, '/api/labels/names', labelsRoute.getAllLabelNames); - apiRoute(GET, '/api/labels/values/:labelName', labelsRoute.getValuesForLabel); - - apiRoute(GET, '/api/notes/:noteId/relations', relationsRoute.getNoteRelations); - apiRoute(PUT, '/api/notes/:noteId/relations', relationsRoute.updateNoteRelations); - apiRoute(GET, '/api/relations/names', relationsRoute.getAllRelationNames); - route(GET, '/api/images/:imageId/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnImage); route(POST, '/api/images', [auth.checkApiAuthOrElectron, uploadMiddleware], imageRoute.uploadImage, apiResultHandler); diff --git a/src/services/attributes.js b/src/services/attributes.js index 0ece703c6..fbd7b3fdb 100644 --- a/src/services/attributes.js +++ b/src/services/attributes.js @@ -22,7 +22,7 @@ const BUILTIN_ATTRIBUTES = [ { type: 'relation', name: 'runOnNoteTitleChange' } ]; -async function getNotesWithAttribute(name, value) { +async function getNotesWithLabel(name, value) { let notes; if (value !== undefined) { @@ -37,8 +37,8 @@ async function getNotesWithAttribute(name, value) { return notes; } -async function getNoteWithAttribute(name, value) { - const notes = await getNotesWithAttribute(name, value); +async function getNoteWithLabel(name, value) { + const notes = await getNotesWithLabel(name, value); return notes.length > 0 ? notes[0] : null; } @@ -70,10 +70,62 @@ async function getAttributeNames(type, nameLike) { 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 = { - getNotesWithAttribute, - getNoteWithAttribute, + getNotesWithLabel, + getNoteWithLabel, createAttribute, getAttributeNames, + getEffectiveAttributes, BUILTIN_ATTRIBUTES }; \ No newline at end of file diff --git a/src/services/script_context.js b/src/services/script_context.js index 52669d49f..3b1f8ee2c 100644 --- a/src/services/script_context.js +++ b/src/services/script_context.js @@ -3,7 +3,7 @@ const noteService = require('./notes'); const sql = require('./sql'); const utils = require('./utils'); const dateUtils = require('./date_utils'); -const labelService = require('./labels'); +const attributeService = require('./attributes'); const dateNoteService = require('./date_notes'); const treeService = require('./tree'); const config = require('./config'); @@ -45,15 +45,14 @@ function ScriptApi(startNote, currentNote, workNote) { this.getNote = repository.getNote; this.getBranch = repository.getBranch; - this.getLabel = repository.getLabel; - this.getRelation = repository.getRelation; + this.getAttribute = repository.getAttribute; this.getImage = repository.getImage; this.getEntity = repository.getEntity; this.getEntities = repository.getEntities; - this.createLabel = labelService.createLabel; - this.getNotesWithLabel = labelService.getNotesWithLabel; - this.getNoteWithLabel = labelService.getNoteWithLabel; + this.createAttribute = attributeService.createAttribute; + this.getNotesWithLabel = attributeService.getNotesWithLabel; + this.getNoteWithLabel = attributeService.getNoteWithLabel; this.createNote = noteService.createNote; diff --git a/src/views/index.ejs b/src/views/index.ejs index 5052bcfbb..928d3f7ed 100644 --- a/src/views/index.ejs +++ b/src/views/index.ejs @@ -168,10 +168,8 @@
@@ -264,20 +262,6 @@ - - @@ -667,105 +651,6 @@ - - - -