diff --git a/db/migrations/0080__rename_attributes_to_labels.sql b/db/migrations/0080__rename_attributes_to_labels.sql new file mode 100644 index 000000000..201922fea --- /dev/null +++ b/db/migrations/0080__rename_attributes_to_labels.sql @@ -0,0 +1,22 @@ +create table labels +( + labelId TEXT not null primary key, + noteId TEXT not null, + name TEXT not null, + value TEXT default '' not null, + position INT default 0 not null, + dateCreated TEXT not null, + dateModified TEXT not null, + isDeleted INT not null +); + +create index IDX_labels_name_value + on labels (name, value); + +create index IDX_labels_noteId + on labels (noteId); + +INSERT INTO labels (labelId, noteId, name, "value", "position", dateCreated, dateModified, isDeleted) + SELECT attributeId, noteId, name, "value", "position", dateCreated, dateModified, isDeleted FROM attributes; + +DROP TABLE attributes; \ No newline at end of file diff --git a/src/entities/attribute.js b/src/entities/label.js similarity index 51% rename from src/entities/attribute.js rename to src/entities/label.js index a82a00216..ccfab2bd2 100644 --- a/src/entities/attribute.js +++ b/src/entities/label.js @@ -2,13 +2,13 @@ const Entity = require('./entity'); -class Attribute extends Entity { - static get tableName() { return "attributes"; } - static get primaryKeyName() { return "attributeId"; } +class Label extends Entity { + static get tableName() { return "labels"; } + static get primaryKeyName() { return "labelId"; } async getNote() { return this.repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); } } -module.exports = Attribute; \ No newline at end of file +module.exports = Label; \ No newline at end of file diff --git a/src/entities/note.js b/src/entities/note.js index a0cb87eec..270df80c7 100644 --- a/src/entities/note.js +++ b/src/entities/note.js @@ -48,30 +48,30 @@ class Note extends Entity { return null; } - async getAttributes() { - return this.repository.getEntities("SELECT * FROM attributes WHERE noteId = ? AND isDeleted = 0", [this.noteId]); + async getLabels() { + return this.repository.getEntities("SELECT * FROM labels WHERE noteId = ? AND isDeleted = 0", [this.noteId]); } - // WARNING: this doesn't take into account the possibility to have multi-valued attributes! - async getAttributeMap() { + // WARNING: this doesn't take into account the possibility to have multi-valued labels! + async getLabelMap() { const map = {}; - for (const attr of await this.getAttributes()) { + for (const attr of await this.getLabels()) { map[attr.name] = attr.value; } return map; } - async hasAttribute(name) { - const map = await this.getAttributeMap(); + async hasLabel(name) { + const map = await this.getLabelMap(); return map.hasOwnProperty(name); } - // WARNING: this doesn't take into account the possibility to have multi-valued attributes! - async getAttribute(name) { - return this.repository.getEntity("SELECT * FROM attributes WHERE noteId = ? AND name = ?", [this.noteId, name]); + // WARNING: this doesn't take into account the possibility to have multi-valued labels! + async getLabel(name) { + return this.repository.getEntity("SELECT * FROM labels WHERE noteId = ? AND name = ?", [this.noteId, name]); } async getRevisions() { diff --git a/src/public/javascripts/dialogs/attributes.js b/src/public/javascripts/dialogs/labels.js similarity index 56% rename from src/public/javascripts/dialogs/attributes.js rename to src/public/javascripts/dialogs/labels.js index a20b003e5..6d1e5d95d 100644 --- a/src/public/javascripts/dialogs/attributes.js +++ b/src/public/javascripts/dialogs/labels.js @@ -1,43 +1,43 @@ "use strict"; -const attributesDialog = (function() { - const $showDialogButton = $(".show-attributes-button"); - const $dialog = $("#attributes-dialog"); - const $saveAttributesButton = $("#save-attributes-button"); - const $attributesBody = $('#attributes-table tbody'); +const labelsDialog = (function() { + const $showDialogButton = $(".show-labels-button"); + const $dialog = $("#labels-dialog"); + const $saveLabelsButton = $("#save-labels-button"); + const $labelsBody = $('#labels-table tbody'); - const attributesModel = new AttributesModel(); - let attributeNames = []; + const labelsModel = new LabelsModel(); + let labelNames = []; - function AttributesModel() { + function LabelsModel() { const self = this; - this.attributes = ko.observableArray(); + this.labels = ko.observableArray(); - this.loadAttributes = async function() { + this.loadLabels = async function() { const noteId = noteEditor.getCurrentNoteId(); - const attributes = await server.get('notes/' + noteId + '/attributes'); + const labels = await server.get('notes/' + noteId + '/labels'); - self.attributes(attributes.map(ko.observable)); + self.labels(labels.map(ko.observable)); addLastEmptyRow(); - attributeNames = await server.get('attributes/names'); + labelNames = await server.get('labels/names'); - // attribute might not be rendered immediatelly so could not focus - setTimeout(() => $(".attribute-name:last").focus(), 100); + // label might not be rendered immediatelly so could not focus + setTimeout(() => $(".label-name:last").focus(), 100); - $attributesBody.sortable({ + $labelsBody.sortable({ handle: '.handle', - containment: $attributesBody, + containment: $labelsBody, update: function() { let position = 0; // we need to update positions by searching in the DOM, because order of the - // attributes in the viewmodel (self.attributes()) stays the same - $attributesBody.find('input[name="position"]').each(function() { - const attr = self.getTargetAttribute(this); + // labels in the viewmodel (self.labels()) stays the same + $labelsBody.find('input[name="position"]').each(function() { + const attr = self.getTargetLabel(this); attr().position = position++; }); @@ -45,8 +45,8 @@ const attributesDialog = (function() { }); }; - this.deleteAttribute = function(data, event) { - const attr = self.getTargetAttribute(event.target); + this.deleteLabel = function(data, event) { + const attr = self.getTargetLabel(event.target); const attrData = attr(); if (attrData) { @@ -59,7 +59,7 @@ const attributesDialog = (function() { }; function isValid() { - for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) { + for (let attrs = self.labels(), i = 0; i < attrs.length; i++) { if (self.isEmptyName(i)) { return false; } @@ -72,7 +72,7 @@ const attributesDialog = (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. - $saveAttributesButton.focus(); + $saveLabelsButton.focus(); if (!isValid()) { alert("Please fix all validation errors and try saving again."); @@ -81,28 +81,28 @@ const attributesDialog = (function() { const noteId = noteEditor.getCurrentNoteId(); - const attributesToSave = self.attributes() + const labelsToSave = self.labels() .map(attr => attr()) - .filter(attr => attr.attributeId !== "" || attr.name !== ""); + .filter(attr => attr.labelId !== "" || attr.name !== ""); - const attributes = await server.put('notes/' + noteId + '/attributes', attributesToSave); + const labels = await server.put('notes/' + noteId + '/labels', labelsToSave); - self.attributes(attributes.map(ko.observable)); + self.labels(labels.map(ko.observable)); addLastEmptyRow(); - showMessage("Attributes have been saved."); + showMessage("Labels have been saved."); - noteEditor.loadAttributeList(); + noteEditor.loadLabelList(); }; function addLastEmptyRow() { - const attrs = self.attributes().filter(attr => attr().isDeleted === 0); + const attrs = self.labels().filter(attr => attr().isDeleted === 0); const last = attrs.length === 0 ? null : attrs[attrs.length - 1](); if (!last || last.name.trim() !== "" || last.value !== "") { - self.attributes.push(ko.observable({ - attributeId: '', + self.labels.push(ko.observable({ + labelId: '', name: '', value: '', isDeleted: 0, @@ -111,22 +111,22 @@ const attributesDialog = (function() { } } - this.attributeChanged = function (data, event) { + this.labelChanged = function (data, event) { addLastEmptyRow(); - const attr = self.getTargetAttribute(event.target); + const attr = self.getTargetLabel(event.target); attr.valueHasMutated(); }; this.isNotUnique = function(index) { - const cur = self.attributes()[index](); + const cur = self.labels()[index](); if (cur.name.trim() === "") { return false; } - for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) { + for (let attrs = self.labels(), i = 0; i < attrs.length; i++) { const attr = attrs[i](); if (index !== i && cur.name === attr.name) { @@ -138,23 +138,23 @@ const attributesDialog = (function() { }; this.isEmptyName = function(index) { - const cur = self.attributes()[index](); + const cur = self.labels()[index](); - return cur.name.trim() === "" && (cur.attributeId !== "" || cur.value !== ""); + return cur.name.trim() === "" && (cur.labelId !== "" || cur.value !== ""); }; - this.getTargetAttribute = function(target) { + this.getTargetLabel = function(target) { const context = ko.contextFor(target); const index = context.$index(); - return self.attributes()[index]; + return self.labels()[index]; } } async function showDialog() { glob.activeDialog = $dialog; - await attributesModel.loadAttributes(); + await labelsModel.loadLabels(); $dialog.dialog({ modal: true, @@ -169,14 +169,14 @@ const attributesDialog = (function() { e.preventDefault(); }); - ko.applyBindings(attributesModel, document.getElementById('attributes-dialog')); + ko.applyBindings(labelsModel, document.getElementById('labels-dialog')); - $(document).on('focus', '.attribute-name', function (e) { + $(document).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 init.js - source: attributeNames.map(attr => { + source: labelNames.map(attr => { return { label: attr, value: attr @@ -189,24 +189,24 @@ const attributesDialog = (function() { $(this).autocomplete("search", $(this).val()); }); - $(document).on('focus', '.attribute-value', async function (e) { + $(document).on('focus', '.label-value', async function (e) { if (!$(this).hasClass("ui-autocomplete-input")) { - const attributeName = $(this).parent().parent().find('.attribute-name').val(); + const labelName = $(this).parent().parent().find('.label-name').val(); - if (attributeName.trim() === "") { + if (labelName.trim() === "") { return; } - const attributeValues = await server.get('attributes/values/' + encodeURIComponent(attributeName)); + const labelValues = await server.get('labels/values/' + encodeURIComponent(labelName)); - if (attributeValues.length === 0) { + 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 init.js - source: attributeValues.map(attr => { + source: labelValues.map(attr => { return { label: attr, value: attr diff --git a/src/public/javascripts/note_editor.js b/src/public/javascripts/note_editor.js index 018db329d..d896243b3 100644 --- a/src/public/javascripts/note_editor.js +++ b/src/public/javascripts/note_editor.js @@ -13,8 +13,8 @@ const noteEditor = (function() { const $unprotectButton = $("#unprotect-button"); const $noteDetailWrapper = $("#note-detail-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 $attachmentFileName = $("#attachment-filename"); const $attachmentFileType = $("#attachment-filetype"); const $attachmentFileSize = $("#attachment-filesize"); @@ -253,8 +253,8 @@ const noteEditor = (function() { else if (currentNote.detail.type === 'file') { $noteDetailAttachment.show(); - $attachmentFileName.text(currentNote.attributes.original_file_name); - $attachmentFileSize.text(currentNote.attributes.file_size + " bytes"); + $attachmentFileName.text(currentNote.labels.original_file_name); + $attachmentFileSize.text(currentNote.labels.file_size + " bytes"); $attachmentFileType.text(currentNote.detail.mime); } else { @@ -269,25 +269,25 @@ const noteEditor = (function() { // after loading new note make sure editor is scrolled to the top $noteDetailWrapper.scrollTop(0); - loadAttributeList(); + loadLabelList(); } - async function loadAttributeList() { + async function loadLabelList() { const noteId = getCurrentNoteId(); - const attributes = await server.get('notes/' + noteId + '/attributes'); + const labels = await server.get('notes/' + noteId + '/labels'); - $attributeListInner.html(''); + $labelListInner.html(''); - if (attributes.length > 0) { - for (const attr of attributes) { - $attributeListInner.append(formatAttribute(attr) + " "); + if (labels.length > 0) { + for (const attr of labels) { + $labelListInner.append(formatLabel(attr) + " "); } - $attributeList.show(); + $labelList.show(); } else { - $attributeList.hide(); + $labelList.hide(); } } @@ -394,7 +394,7 @@ const noteEditor = (function() { getEditor, focus, executeCurrentNote, - loadAttributeList, + loadLabelList, setContent }; })(); \ No newline at end of file diff --git a/src/public/javascripts/utils.js b/src/public/javascripts/utils.js index 0fcb36682..41f66f204 100644 --- a/src/public/javascripts/utils.js +++ b/src/public/javascripts/utils.js @@ -125,7 +125,7 @@ function formatValueWithWhitespace(val) { return /[^\w_-]/.test(val) ? '"' + val + '"' : val; } -function formatAttribute(attr) { +function formatLabel(attr) { let str = "@" + formatValueWithWhitespace(attr.name); if (attr.value !== "") { diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index fcf148a2a..c9229ebec 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -8,7 +8,7 @@ "search note-content" "tree note-content" "parent-list note-content" - "parent-list attribute-list"; + "parent-list label-list"; grid-template-columns: 2fr 5fr; grid-template-rows: auto auto @@ -269,14 +269,14 @@ div.ui-tooltip { .cm-matchhighlight {background-color: #eeeeee} -#attribute-list { - grid-area: attribute-list; +#label-list { + grid-area: label-list; color: #777777; border-top: 1px solid #eee; padding: 5px; display: none; } -#attribute-list button { +#label-list button { padding: 2px; margin-right: 5px; } diff --git a/src/routes/api/attachments.js b/src/routes/api/attachments.js index dfa3822a0..deb3de4b3 100644 --- a/src/routes/api/attachments.js +++ b/src/routes/api/attachments.js @@ -5,7 +5,7 @@ const router = express.Router(); const sql = require('../../services/sql'); const auth = require('../../services/auth'); const notes = require('../../services/notes'); -const attributes = require('../../services/attributes'); +const labels = require('../../services/labels'); const protected_session = require('../../services/protected_session'); const multer = require('multer')(); const wrap = require('express-promise-wrap').wrap; @@ -33,8 +33,8 @@ router.post('/upload/:parentNoteId', auth.checkApiAuthOrElectron, multer.single( mime: file.mimetype }, req, sourceId)).noteId; - await attributes.createAttribute(noteId, "original_file_name", originalName, sourceId); - await attributes.createAttribute(noteId, "file_size", size, sourceId); + await labels.createLabel(noteId, "original_file_name", originalName, sourceId); + await labels.createLabel(noteId, "file_size", size, sourceId); res.send({ noteId: noteId @@ -62,8 +62,8 @@ router.get('/download/:noteId', auth.checkApiAuthOrElectron, wrap(async (req, re protected_session.decryptNote(dataKey, note); } - const attributeMap = await attributes.getNoteAttributeMap(noteId); - const fileName = attributeMap.original_file_name ? attributeMap.original_file_name : note.title; + const labelMap = await labels.getNoteLabelMap(noteId); + const fileName = labelMap.original_file_name ? labelMap.original_file_name : note.title; res.setHeader('Content-Disposition', 'attachment; filename=' + fileName); res.setHeader('Content-Type', note.mime); diff --git a/src/routes/api/cleanup.js b/src/routes/api/cleanup.js index fa0475cd9..b3afa8c37 100644 --- a/src/routes/api/cleanup.js +++ b/src/routes/api/cleanup.js @@ -22,7 +22,7 @@ router.post('/cleanup-soft-deleted-items', auth.checkApiAuth, wrap(async (req, r await sql.execute(`DELETE FROM note_images WHERE noteId IN (${noteIdsSql})`); - await sql.execute(`DELETE FROM attributes WHERE noteId IN (${noteIdsSql})`); + await sql.execute(`DELETE FROM labels WHERE noteId IN (${noteIdsSql})`); await sql.execute("DELETE FROM branches WHERE isDeleted = 1"); diff --git a/src/routes/api/export.js b/src/routes/api/export.js index b2f1b4881..a4775d02b 100644 --- a/src/routes/api/export.js +++ b/src/routes/api/export.js @@ -38,7 +38,7 @@ async function exportNote(branchId, directory, pack, repo) { const metadata = await getMetadata(note); - if (metadata.attributes.find(attr => attr.name === 'exclude_from_export')) { + if (metadata.labels.find(attr => attr.name === 'exclude_from_export')) { return; } @@ -68,7 +68,7 @@ async function getMetadata(note) { title: note.title, type: note.type, mime: note.mime, - attributes: (await note.getAttributes()).map(attr => { + labels: (await note.getLabels()).map(attr => { return { name: attr.name, value: attr.value diff --git a/src/routes/api/import.js b/src/routes/api/import.js index dfbed493e..dcc2dc5e3 100644 --- a/src/routes/api/import.js +++ b/src/routes/api/import.js @@ -4,7 +4,7 @@ const express = require('express'); const router = express.Router(); const sql = require('../../services/sql'); const auth = require('../../services/auth'); -const attributes = require('../../services/attributes'); +const labels = require('../../services/labels'); const notes = require('../../services/notes'); const wrap = require('express-promise-wrap').wrap; const tar = require('tar-stream'); @@ -126,8 +126,8 @@ async function importNotes(files, parentNoteId, sourceId) { sourceId: sourceId }); - for (const attr of file.meta.attributes) { - await attributes.createAttribute(noteId, attr.name, attr.value); + for (const attr of file.meta.labels) { + await labels.createLabel(noteId, attr.name, attr.value); } if (file.children.length > 0) { diff --git a/src/routes/api/attributes.js b/src/routes/api/labels.js similarity index 50% rename from src/routes/api/attributes.js rename to src/routes/api/labels.js index dfaae158a..5c796e940 100644 --- a/src/routes/api/attributes.js +++ b/src/routes/api/labels.js @@ -7,24 +7,24 @@ const auth = require('../../services/auth'); const sync_table = require('../../services/sync_table'); const utils = require('../../services/utils'); const wrap = require('express-promise-wrap').wrap; -const attributes = require('../../services/attributes'); +const labels = require('../../services/labels'); -router.get('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => { +router.get('/notes/:noteId/labels', auth.checkApiAuth, wrap(async (req, res, next) => { const noteId = req.params.noteId; - res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId])); + res.send(await sql.getRows("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId])); })); -router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => { +router.put('/notes/:noteId/labels', auth.checkApiAuth, wrap(async (req, res, next) => { const noteId = req.params.noteId; - const attributes = req.body; + const labels = req.body; const now = utils.nowDate(); await sql.doInTransaction(async () => { - for (const attr of attributes) { - if (attr.attributeId) { - await sql.execute("UPDATE attributes SET name = ?, value = ?, dateModified = ?, isDeleted = ?, position = ? WHERE attributeId = ?", - [attr.name, attr.value, now, attr.isDeleted, attr.position, attr.attributeId]); + for (const attr of labels) { + if (attr.labelId) { + await sql.execute("UPDATE labels SET name = ?, value = ?, dateModified = ?, isDeleted = ?, position = ? WHERE labelId = ?", + [attr.name, attr.value, now, attr.isDeleted, attr.position, attr.labelId]); } else { // if it was "created" and then immediatelly deleted, we just don't create it at all @@ -32,10 +32,10 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, continue; } - attr.attributeId = utils.newAttributeId(); + attr.labelId = utils.newLabelId(); - await sql.insert("attributes", { - attributeId: attr.attributeId, + await sql.insert("labels", { + labelId: attr.labelId, noteId: noteId, name: attr.name, value: attr.value, @@ -46,17 +46,17 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, }); } - await sync_table.addAttributeSync(attr.attributeId); + await sync_table.addLabelSync(attr.labelId); } }); - res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId])); + res.send(await sql.getRows("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId])); })); -router.get('/attributes/names', auth.checkApiAuth, wrap(async (req, res, next) => { - const names = await sql.getColumn("SELECT DISTINCT name FROM attributes WHERE isDeleted = 0"); +router.get('/labels/names', auth.checkApiAuth, wrap(async (req, res, next) => { + const names = await sql.getColumn("SELECT DISTINCT name FROM labels WHERE isDeleted = 0"); - for (const attr of attributes.BUILTIN_ATTRIBUTES) { + for (const attr of labels.BUILTIN_LABELS) { if (!names.includes(attr)) { names.push(attr); } @@ -67,10 +67,10 @@ router.get('/attributes/names', auth.checkApiAuth, wrap(async (req, res, next) = res.send(names); })); -router.get('/attributes/values/:attributeName', auth.checkApiAuth, wrap(async (req, res, next) => { - const attributeName = req.params.attributeName; +router.get('/labels/values/:labelName', auth.checkApiAuth, wrap(async (req, res, next) => { + const labelName = req.params.labelName; - const values = await sql.getColumn("SELECT DISTINCT value FROM attributes WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [attributeName]); + const values = await sql.getColumn("SELECT DISTINCT value FROM labels WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [labelName]); res.send(values); })); diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js index e0e7c5b8c..138fd828a 100644 --- a/src/routes/api/notes.js +++ b/src/routes/api/notes.js @@ -5,7 +5,7 @@ const router = express.Router(); const auth = require('../../services/auth'); const sql = require('../../services/sql'); const notes = require('../../services/notes'); -const attributes = require('../../services/attributes'); +const labels = require('../../services/labels'); const log = require('../../services/log'); const utils = require('../../services/utils'); const protected_session = require('../../services/protected_session'); @@ -26,19 +26,19 @@ router.get('/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => { protected_session.decryptNote(req, detail); - let attributeMap = null; + let labelMap = null; if (detail.type === 'file') { // no need to transfer attachment payload for this request detail.content = null; - // attributes contain important attachment metadata - filename and size - attributeMap = await attributes.getNoteAttributeMap(noteId); + // labels contain important attachment metadata - filename and size + labelMap = await labels.getNoteLabelMap(noteId); } res.send({ detail: detail, - attributes: attributeMap + labels: labelMap }); })); diff --git a/src/routes/api/script.js b/src/routes/api/script.js index fab7ef76b..b644e09e4 100644 --- a/src/routes/api/script.js +++ b/src/routes/api/script.js @@ -4,7 +4,7 @@ const express = require('express'); const router = express.Router(); const auth = require('../../services/auth'); const wrap = require('express-promise-wrap').wrap; -const attributes = require('../../services/attributes'); +const labels = require('../../services/labels'); const script = require('../../services/script'); const Repository = require('../../services/repository'); @@ -29,7 +29,7 @@ router.post('/run/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => { router.get('/startup', auth.checkApiAuth, wrap(async (req, res, next) => { const repository = new Repository(req); - const notes = await attributes.getNotesWithAttribute(repository, "run", "frontend_startup"); + const notes = await labels.getNotesWithLabel(repository, "run", "frontend_startup"); const scripts = []; diff --git a/src/routes/api/sync.js b/src/routes/api/sync.js index 756e3a87e..ceeaf0ae1 100644 --- a/src/routes/api/sync.js +++ b/src/routes/api/sync.js @@ -144,10 +144,10 @@ router.get('/note_images/:noteImageId', auth.checkApiAuth, wrap(async (req, res, res.send(await sql.getRow("SELECT * FROM note_images WHERE noteImageId = ?", [noteImageId])); })); -router.get('/attributes/:attributeId', auth.checkApiAuth, wrap(async (req, res, next) => { - const attributeId = req.params.attributeId; +router.get('/labels/:labelId', auth.checkApiAuth, wrap(async (req, res, next) => { + const labelId = req.params.labelId; - res.send(await sql.getRow("SELECT * FROM attributes WHERE attributeId = ?", [attributeId])); + res.send(await sql.getRow("SELECT * FROM labels WHERE labelId = ?", [labelId])); })); router.get('/api_tokens/:apiTokenId', auth.checkApiAuth, wrap(async (req, res, next) => { @@ -204,8 +204,8 @@ router.put('/note_images', auth.checkApiAuth, wrap(async (req, res, next) => { res.send({}); })); -router.put('/attributes', auth.checkApiAuth, wrap(async (req, res, next) => { - await syncUpdate.updateAttribute(req.body.entity, req.body.sourceId); +router.put('/labels', auth.checkApiAuth, wrap(async (req, res, next) => { + await syncUpdate.updateLabel(req.body.entity, req.body.sourceId); res.send({}); })); diff --git a/src/routes/api/tree.js b/src/routes/api/tree.js index f4c661484..9d16e022b 100644 --- a/src/routes/api/tree.js +++ b/src/routes/api/tree.js @@ -42,10 +42,10 @@ router.get('/', auth.checkApiAuth, wrap(async (req, res, next) => { notes.isProtected, notes.type, notes.mime, - hideInAutocomplete.attributeId AS 'hideInAutocomplete' + hideInAutocomplete.labelId AS 'hideInAutocomplete' FROM notes - LEFT JOIN attributes AS hideInAutocomplete ON hideInAutocomplete.noteId = notes.noteId + LEFT JOIN labels AS hideInAutocomplete ON hideInAutocomplete.noteId = notes.noteId AND hideInAutocomplete.name = 'hide_in_autocomplete' AND hideInAutocomplete.isDeleted = 0 WHERE diff --git a/src/routes/api/tree_changes.js b/src/routes/api/tree_changes.js index 015b68999..f0db162ba 100644 --- a/src/routes/api/tree_changes.js +++ b/src/routes/api/tree_changes.js @@ -106,7 +106,7 @@ router.put('/:branchId/expanded/:expanded', auth.checkApiAuth, wrap(async (req, await sql.doInTransaction(async () => { await sql.execute("UPDATE branches SET isExpanded = ? WHERE branchId = ?", [expanded, branchId]); - // we don't sync expanded attribute + // we don't sync expanded label }); res.send({}); diff --git a/src/routes/index.js b/src/routes/index.js index f32786ef8..479294652 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -6,7 +6,7 @@ const auth = require('../services/auth'); const source_id = require('../services/source_id'); const sql = require('../services/sql'); const Repository = require('../services/repository'); -const attributes = require('../services/attributes'); +const labels = require('../services/labels'); const wrap = require('express-promise-wrap').wrap; router.get('', auth.checkAuth, wrap(async (req, res, next) => { @@ -21,7 +21,7 @@ router.get('', auth.checkAuth, wrap(async (req, res, next) => { async function getAppCss(repository) { let css = ''; - const notes = attributes.getNotesWithAttribute(repository, 'app_css'); + const notes = labels.getNotesWithLabel(repository, 'app_css'); for (const note of await notes) { css += `/* ${note.noteId} */ diff --git a/src/routes/routes.js b/src/routes/routes.js index 8d7055cdd..004a22cec 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -26,7 +26,7 @@ const sqlRoute = require('./api/sql'); 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 scriptRoute = require('./api/script'); const senderRoute = require('./api/sender'); const attachmentsRoute = require('./api/attachments'); @@ -43,7 +43,7 @@ function register(app) { app.use('/api/notes', notesApiRoute); app.use('/api/tree', treeChangesApiRoute); app.use('/api/notes', cloningApiRoute); - app.use('/api', attributesRoute); + app.use('/api', labelsRoute); app.use('/api/notes-history', noteHistoryApiRoute); app.use('/api/recent-changes', recentChangesApiRoute); app.use('/api/settings', settingsApiRoute); diff --git a/src/scripts/Reddit Importer.tar b/src/scripts/Reddit Importer.tar index 7435750ff..ca6170ed2 100644 Binary files a/src/scripts/Reddit Importer.tar and b/src/scripts/Reddit Importer.tar differ diff --git a/src/scripts/Today.tar b/src/scripts/Today.tar index 761eb17aa..04f5f6dce 100644 Binary files a/src/scripts/Today.tar and b/src/scripts/Today.tar differ diff --git a/src/scripts/Weight Tracker.tar b/src/scripts/Weight Tracker.tar index 591f41f53..2e3b4f44b 100644 Binary files a/src/scripts/Weight Tracker.tar and b/src/scripts/Weight Tracker.tar differ diff --git a/src/services/app_info.js b/src/services/app_info.js index 97e460ca5..f49ee7980 100644 --- a/src/services/app_info.js +++ b/src/services/app_info.js @@ -3,7 +3,7 @@ const build = require('./build'); const packageJson = require('../../package'); -const APP_DB_VERSION = 79; +const APP_DB_VERSION = 80; module.exports = { app_version: packageJson.version, diff --git a/src/services/attributes.js b/src/services/attributes.js deleted file mode 100644 index 0d760d35c..000000000 --- a/src/services/attributes.js +++ /dev/null @@ -1,89 +0,0 @@ -"use strict"; - -const sql = require('./sql'); -const utils = require('./utils'); -const sync_table = require('./sync_table'); - -const BUILTIN_ATTRIBUTES = [ - 'frontend_startup', - 'backend_startup', - 'disable_versioning', - 'calendar_root', - 'hide_in_autocomplete', - 'exclude_from_export', - 'run', - 'manual_transaction_handling', - 'disable_inclusion', - 'app_css' -]; - -async function getNoteAttributeMap(noteId) { - return await sql.getMap(`SELECT name, value FROM attributes WHERE noteId = ? AND isDeleted = 0`, [noteId]); -} - -async function getNoteIdWithAttribute(name, value) { - return await sql.getValue(`SELECT notes.noteId FROM notes JOIN attributes USING(noteId) - WHERE notes.isDeleted = 0 - AND attributes.isDeleted = 0 - AND attributes.name = ? - AND attributes.value = ?`, [name, value]); -} - -async function getNotesWithAttribute(repository, name, value) { - let notes; - - if (value !== undefined) { - notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN attributes USING(noteId) - WHERE notes.isDeleted = 0 AND attributes.isDeleted = 0 AND attributes.name = ? AND attributes.value = ?`, [name, value]); - } - else { - notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN attributes USING(noteId) - WHERE notes.isDeleted = 0 AND attributes.isDeleted = 0 AND attributes.name = ?`, [name]); - } - - return notes; -} - -async function getNoteWithAttribute(repository, name, value) { - const notes = getNotesWithAttribute(repository, name, value); - - return notes.length > 0 ? notes[0] : null; -} - -async function getNoteIdsWithAttribute(name) { - return await sql.getColumn(`SELECT DISTINCT notes.noteId FROM notes JOIN attributes USING(noteId) - WHERE notes.isDeleted = 0 AND attributes.isDeleted = 0 AND attributes.name = ? AND attributes.isDeleted = 0`, [name]); -} - -async function createAttribute(noteId, name, value = "", sourceId = null) { - if (value === null || value === undefined) { - value = ""; - } - - const now = utils.nowDate(); - const attributeId = utils.newAttributeId(); - const position = 1 + await sql.getValue(`SELECT COALESCE(MAX(position), 0) FROM attributes WHERE noteId = ?`, [noteId]); - - await sql.insert("attributes", { - attributeId: attributeId, - noteId: noteId, - name: name, - value: value, - position: position, - dateModified: now, - dateCreated: now, - isDeleted: false - }); - - await sync_table.addAttributeSync(attributeId, sourceId); -} - -module.exports = { - getNoteAttributeMap, - getNoteIdWithAttribute, - getNotesWithAttribute, - getNoteWithAttribute, - getNoteIdsWithAttribute, - createAttribute, - BUILTIN_ATTRIBUTES -}; \ No newline at end of file diff --git a/src/services/build_search_query.js b/src/services/build_search_query.js index e877cd184..1ae067acc 100644 --- a/src/services/build_search_query.js +++ b/src/services/build_search_query.js @@ -7,16 +7,16 @@ module.exports = function(attrFilters, searchText) { let i = 1; for (const filter of attrFilters) { - joins.push(`LEFT JOIN attributes AS attr${i} ON attr${i}.noteId = notes.noteId AND attr${i}.name = ?`); + joins.push(`LEFT JOIN labels AS attr${i} ON attr${i}.noteId = notes.noteId AND attr${i}.name = ?`); joinParams.push(filter.name); where += " " + filter.relation + " "; if (filter.operator === 'exists') { - where += `attr${i}.attributeId IS NOT NULL`; + where += `attr${i}.labelId IS NOT NULL`; } else if (filter.operator === 'not-exists') { - where += `attr${i}.attributeId IS NULL`; + where += `attr${i}.labelId IS NULL`; } else if (filter.operator === '=' || filter.operator === '!=') { where += `attr${i}.value ${filter.operator} ?`; diff --git a/src/services/consistency_checks.js b/src/services/consistency_checks.js index 4112a7ab1..adf14fe4a 100644 --- a/src/services/consistency_checks.js +++ b/src/services/consistency_checks.js @@ -233,7 +233,7 @@ async function runAllChecks() { await runSyncRowChecks("recent_notes", "branchId", errorList); await runSyncRowChecks("images", "imageId", errorList); await runSyncRowChecks("note_images", "noteImageId", errorList); - await runSyncRowChecks("attributes", "attributeId", errorList); + await runSyncRowChecks("labels", "labelId", errorList); await runSyncRowChecks("api_tokens", "apiTokenId", errorList); if (errorList.length === 0) { diff --git a/src/services/content_hash.js b/src/services/content_hash.js index 7ae550c11..3c99030d1 100644 --- a/src/services/content_hash.js +++ b/src/services/content_hash.js @@ -83,16 +83,16 @@ async function getHashes() { FROM images ORDER BY imageId`)), - attributes: getHash(await sql.getRows(` + labels: getHash(await sql.getRows(` SELECT - attributeId, + labelId, noteId name, value dateModified, dateCreated - FROM attributes - ORDER BY attributeId`)) + FROM labels + ORDER BY labelId`)) }; const elapseTimeMs = new Date().getTime() - startTime.getTime(); diff --git a/src/services/date_notes.js b/src/services/date_notes.js index 3fb6514d3..ad75e8bec 100644 --- a/src/services/date_notes.js +++ b/src/services/date_notes.js @@ -2,13 +2,13 @@ const sql = require('./sql'); const notes = require('./notes'); -const attributes = require('./attributes'); +const labels = require('./labels'); const utils = require('./utils'); -const CALENDAR_ROOT_ATTRIBUTE = 'calendar_root'; -const YEAR_ATTRIBUTE = 'year_note'; -const MONTH_ATTRIBUTE = 'month_note'; -const DATE_ATTRIBUTE = 'date_note'; +const CALENDAR_ROOT_LABEL = 'calendar_root'; +const YEAR_LABEL = 'year_note'; +const MONTH_LABEL = 'month_note'; +const DATE_LABEL = 'date_note'; const DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December']; @@ -30,8 +30,8 @@ async function getNoteStartingWith(parentNoteId, startsWith) { } async function getRootCalendarNoteId() { - let rootNoteId = await sql.getValue(`SELECT notes.noteId FROM notes JOIN attributes USING(noteId) - WHERE attributes.name = '${CALENDAR_ROOT_ATTRIBUTE}' AND notes.isDeleted = 0`); + let rootNoteId = await sql.getValue(`SELECT notes.noteId FROM notes JOIN labels USING(noteId) + WHERE labels.name = '${CALENDAR_ROOT_LABEL}' AND notes.isDeleted = 0`); if (!rootNoteId) { rootNoteId = (await notes.createNewNote('root', { @@ -40,7 +40,7 @@ async function getRootCalendarNoteId() { isProtected: false })).noteId; - await attributes.createAttribute(rootNoteId, CALENDAR_ROOT_ATTRIBUTE); + await labels.createLabel(rootNoteId, CALENDAR_ROOT_LABEL); } return rootNoteId; @@ -49,7 +49,7 @@ async function getRootCalendarNoteId() { async function getYearNoteId(dateTimeStr, rootNoteId) { const yearStr = dateTimeStr.substr(0, 4); - let yearNoteId = await attributes.getNoteIdWithAttribute(YEAR_ATTRIBUTE, yearStr); + let yearNoteId = await labels.getNoteIdWithLabel(YEAR_LABEL, yearStr); if (!yearNoteId) { yearNoteId = await getNoteStartingWith(rootNoteId, yearStr); @@ -58,7 +58,7 @@ async function getYearNoteId(dateTimeStr, rootNoteId) { yearNoteId = await createNote(rootNoteId, yearStr); } - await attributes.createAttribute(yearNoteId, YEAR_ATTRIBUTE, yearStr); + await labels.createLabel(yearNoteId, YEAR_LABEL, yearStr); } return yearNoteId; @@ -68,7 +68,7 @@ async function getMonthNoteId(dateTimeStr, rootNoteId) { const monthStr = dateTimeStr.substr(0, 7); const monthNumber = dateTimeStr.substr(5, 2); - let monthNoteId = await attributes.getNoteIdWithAttribute(MONTH_ATTRIBUTE, monthStr); + let monthNoteId = await labels.getNoteIdWithLabel(MONTH_LABEL, monthStr); if (!monthNoteId) { const yearNoteId = await getYearNoteId(dateTimeStr, rootNoteId); @@ -83,7 +83,7 @@ async function getMonthNoteId(dateTimeStr, rootNoteId) { monthNoteId = await createNote(yearNoteId, noteTitle); } - await attributes.createAttribute(monthNoteId, MONTH_ATTRIBUTE, monthStr); + await labels.createLabel(monthNoteId, MONTH_LABEL, monthStr); } return monthNoteId; @@ -97,7 +97,7 @@ async function getDateNoteId(dateTimeStr, rootNoteId = null) { const dateStr = dateTimeStr.substr(0, 10); const dayNumber = dateTimeStr.substr(8, 2); - let dateNoteId = await attributes.getNoteIdWithAttribute(DATE_ATTRIBUTE, dateStr); + let dateNoteId = await labels.getNoteIdWithLabel(DATE_LABEL, dateStr); if (!dateNoteId) { const monthNoteId = await getMonthNoteId(dateTimeStr, rootNoteId); @@ -112,7 +112,7 @@ async function getDateNoteId(dateTimeStr, rootNoteId = null) { dateNoteId = await createNote(monthNoteId, noteTitle); } - await attributes.createAttribute(dateNoteId, DATE_ATTRIBUTE, dateStr); + await labels.createLabel(dateNoteId, DATE_LABEL, dateStr); } return dateNoteId; diff --git a/src/services/labels.js b/src/services/labels.js new file mode 100644 index 000000000..c2f169e55 --- /dev/null +++ b/src/services/labels.js @@ -0,0 +1,89 @@ +"use strict"; + +const sql = require('./sql'); +const utils = require('./utils'); +const sync_table = require('./sync_table'); + +const BUILTIN_LABELS = [ + 'frontend_startup', + 'backend_startup', + 'disable_versioning', + 'calendar_root', + 'hide_in_autocomplete', + 'exclude_from_export', + 'run', + 'manual_transaction_handling', + 'disable_inclusion', + 'app_css' +]; + +async function getNoteLabelMap(noteId) { + return await sql.getMap(`SELECT name, value FROM labels WHERE noteId = ? AND isDeleted = 0`, [noteId]); +} + +async function getNoteIdWithLabel(name, value) { + return await sql.getValue(`SELECT notes.noteId FROM notes JOIN labels USING(noteId) + WHERE notes.isDeleted = 0 + AND labels.isDeleted = 0 + AND labels.name = ? + AND labels.value = ?`, [name, value]); +} + +async function getNotesWithLabel(repository, 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(repository, name, value) { + const notes = getNotesWithLabel(repository, name, value); + + return notes.length > 0 ? notes[0] : null; +} + +async function getNoteIdsWithLabel(name) { + return await sql.getColumn(`SELECT DISTINCT notes.noteId FROM notes JOIN labels USING(noteId) + WHERE notes.isDeleted = 0 AND labels.isDeleted = 0 AND labels.name = ? AND labels.isDeleted = 0`, [name]); +} + +async function createLabel(noteId, name, value = "", sourceId = null) { + if (value === null || value === undefined) { + value = ""; + } + + const now = utils.nowDate(); + const labelId = utils.newLabelId(); + const position = 1 + await sql.getValue(`SELECT COALESCE(MAX(position), 0) FROM labels WHERE noteId = ?`, [noteId]); + + await sql.insert("labels", { + labelId: labelId, + noteId: noteId, + name: name, + value: value, + position: position, + dateModified: now, + dateCreated: now, + isDeleted: false + }); + + await sync_table.addLabelSync(labelId, sourceId); +} + +module.exports = { + getNoteLabelMap, + getNoteIdWithLabel, + getNotesWithLabel, + getNoteWithLabel, + getNoteIdsWithLabel, + createLabel, + BUILTIN_LABELS +}; \ No newline at end of file diff --git a/src/services/notes.js b/src/services/notes.js index cfe50e12e..e5b52fd97 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -2,7 +2,7 @@ const sql = require('./sql'); const options = require('./options'); const utils = require('./utils'); const sync_table = require('./sync_table'); -const attributes = require('./attributes'); +const labels = require('./labels'); const protected_session = require('./protected_session'); async function createNewNote(parentNoteId, noteOpts, dataKey, sourceId) { @@ -108,9 +108,9 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {}) const {noteId} = await createNewNote(parentNoteId, note, extraOptions.dataKey, extraOptions.sourceId); - if (extraOptions.attributes) { - for (const attrName in extraOptions.attributes) { - await attributes.createAttribute(noteId, attrName, extraOptions.attributes[attrName]); + if (extraOptions.labels) { + for (const attrName in extraOptions.labels) { + await labels.createLabel(noteId, attrName, extraOptions.labels[attrName]); } } @@ -274,7 +274,7 @@ async function updateNote(noteId, newNote, dataKey, sourceId) { await protected_session.encryptNote(dataKey, newNote.detail); } - const attributesMap = await attributes.getNoteAttributeMap(noteId); + const labelsMap = await labels.getNoteLabelMap(noteId); const now = new Date(); const nowStr = utils.nowDate(); @@ -289,7 +289,7 @@ async function updateNote(noteId, newNote, dataKey, sourceId) { await sql.doInTransaction(async () => { const msSinceDateCreated = now.getTime() - utils.parseDateTime(newNote.detail.dateCreated).getTime(); - if (attributesMap.disable_versioning !== 'true' + if (labelsMap.disable_versioning !== 'true' && !existingnoteRevisionId && msSinceDateCreated >= historySnapshotTimeInterval * 1000) { diff --git a/src/services/parse_filters.js b/src/services/parse_filters.js index 2235d6e7d..59b294894 100644 --- a/src/services/parse_filters.js +++ b/src/services/parse_filters.js @@ -18,7 +18,7 @@ module.exports = function(searchText) { value: match[7] !== undefined ? trimQuotes(match[7]) : null }); - // remove attributes from further fulltext search + // remove labels from further fulltext search searchText = searchText.split(match[0]).join(''); match = attrRegex.exec(searchText); diff --git a/src/services/repository.js b/src/services/repository.js index 44ff2dc3f..9caae4c97 100644 --- a/src/services/repository.js +++ b/src/services/repository.js @@ -3,7 +3,7 @@ const protected_session = require('./protected_session'); const Note = require('../entities/note'); const NoteRevision = require('../entities/note_revision'); const Branch = require('../entities/branch'); -const Attribute = require('../entities/attribute'); +const Label = require('../entities/label'); const sync_table = require('../services/sync_table'); class Repository { @@ -40,8 +40,8 @@ class Repository { createEntityFromRow(row) { let entity; - if (row.attributeId) { - entity = new Attribute(this, row); + if (row.labelId) { + entity = new Label(this, row); } else if (row.noteRevisionId) { entity = new NoteRevision(this, row); diff --git a/src/services/scheduler.js b/src/services/scheduler.js index 3c6d4d85a..26e0083ef 100644 --- a/src/services/scheduler.js +++ b/src/services/scheduler.js @@ -3,14 +3,14 @@ const Repository = require('./repository'); const repo = new Repository(); -async function runNotesWithAttribute(runAttrValue) { +async function runNotesWithLabel(runAttrValue) { const notes = await repo.getEntities(` SELECT notes.* FROM notes - JOIN attributes ON attributes.noteId = notes.noteId - AND attributes.isDeleted = 0 - AND attributes.name = 'run' - AND attributes.value = ? + JOIN labels ON labels.noteId = notes.noteId + AND labels.isDeleted = 0 + AND labels.name = 'run' + AND labels.value = ? WHERE notes.type = 'code' AND notes.isDeleted = 0`, [runAttrValue]); @@ -20,8 +20,8 @@ async function runNotesWithAttribute(runAttrValue) { } } -setTimeout(() => runNotesWithAttribute('backend_startup'), 10 * 1000); +setTimeout(() => runNotesWithLabel('backend_startup'), 10 * 1000); -setInterval(() => runNotesWithAttribute('hourly'), 3600 * 1000); +setInterval(() => runNotesWithLabel('hourly'), 3600 * 1000); -setInterval(() => runNotesWithAttribute('daily'), 24 * 3600 * 1000); \ No newline at end of file +setInterval(() => runNotesWithLabel('daily'), 24 * 3600 * 1000); \ No newline at end of file diff --git a/src/services/script.js b/src/services/script.js index b8676c3ec..200815f5b 100644 --- a/src/services/script.js +++ b/src/services/script.js @@ -23,7 +23,7 @@ async function executeBundle(dataKey, bundle, startNote) { const ctx = new ScriptContext(dataKey, startNote, bundle.allNotes); - if (await bundle.note.hasAttribute('manual_transaction_handling')) { + if (await bundle.note.hasLabel('manual_transaction_handling')) { return await execute(ctx, script, ''); } else { @@ -73,7 +73,7 @@ async function getScriptBundle(note, root = true, scriptEnv = null, includedNote return; } - if (!root && await note.hasAttribute('disable_inclusion')) { + if (!root && await note.hasLabel('disable_inclusion')) { return; } diff --git a/src/services/script_context.js b/src/services/script_context.js index 3dd375305..ecd25dfbe 100644 --- a/src/services/script_context.js +++ b/src/services/script_context.js @@ -3,7 +3,7 @@ const protected_session = require('./protected_session'); const notes = require('./notes'); const sql = require('./sql'); const utils = require('./utils'); -const attributes = require('./attributes'); +const labels = require('./labels'); const date_notes = require('./date_notes'); const config = require('./config'); const Repository = require('./repository'); @@ -48,12 +48,12 @@ function ScriptApi(dataKey, startNote, currentNote) { return repository.getNote(noteId); }; - this.getNotesWithAttribute = async function (attrName, attrValue) { - return await attributes.getNotesWithAttribute(repository, attrName, attrValue); + this.getNotesWithLabel = async function (attrName, attrValue) { + return await labels.getNotesWithLabel(repository, attrName, attrValue); }; - this.getNoteWithAttribute = async function (attrName, attrValue) { - const notes = await this.getNotesWithAttribute(attrName, attrValue); + this.getNoteWithLabel = async function (attrName, attrValue) { + const notes = await this.getNotesWithLabel(attrName, attrValue); return notes.length > 0 ? notes[0] : null; }; @@ -64,7 +64,7 @@ function ScriptApi(dataKey, startNote, currentNote) { return await notes.createNote(parentNoteId, title, content, extraOptions); }; - this.createAttribute = attributes.createAttribute; + this.createLabel = labels.createLabel; this.updateEntity = repository.updateEntity; diff --git a/src/services/sync.js b/src/services/sync.js index 325fceb9b..a990183e4 100644 --- a/src/services/sync.js +++ b/src/services/sync.js @@ -146,8 +146,8 @@ async function pullSync(syncContext) { else if (sync.entityName === 'note_images') { await syncUpdate.updateNoteImage(resp, syncContext.sourceId); } - else if (sync.entityName === 'attributes') { - await syncUpdate.updateAttribute(resp, syncContext.sourceId); + else if (sync.entityName === 'labels') { + await syncUpdate.updateLabel(resp, syncContext.sourceId); } else if (sync.entityName === 'api_tokens') { await syncUpdate.updateApiToken(resp, syncContext.sourceId); @@ -235,8 +235,8 @@ async function pushEntity(sync, syncContext) { else if (sync.entityName === 'note_images') { entity = await sql.getRow('SELECT * FROM note_images WHERE noteImageId = ?', [sync.entityId]); } - else if (sync.entityName === 'attributes') { - entity = await sql.getRow('SELECT * FROM attributes WHERE attributeId = ?', [sync.entityId]); + else if (sync.entityName === 'labels') { + entity = await sql.getRow('SELECT * FROM labels WHERE labelId = ?', [sync.entityId]); } else if (sync.entityName === 'api_tokens') { entity = await sql.getRow('SELECT * FROM api_tokens WHERE apiTokenId = ?', [sync.entityId]); diff --git a/src/services/sync_table.js b/src/services/sync_table.js index 5a47e7ded..6ff0a2f50 100644 --- a/src/services/sync_table.js +++ b/src/services/sync_table.js @@ -36,8 +36,8 @@ async function addNoteImageSync(noteImageId, sourceId) { await addEntitySync("note_images", noteImageId, sourceId); } -async function addAttributeSync(attributeId, sourceId) { - await addEntitySync("attributes", attributeId, sourceId); +async function addLabelSync(labelId, sourceId) { + await addEntitySync("labels", labelId, sourceId); } async function addApiTokenSync(apiTokenId, sourceId) { @@ -96,7 +96,7 @@ async function fillAllSyncRows() { await fillSyncRows("recent_notes", "branchId"); await fillSyncRows("images", "imageId"); await fillSyncRows("note_images", "noteImageId"); - await fillSyncRows("attributes", "attributeId"); + await fillSyncRows("labels", "labelId"); await fillSyncRows("api_tokens", "apiTokenId"); } @@ -109,7 +109,7 @@ module.exports = { addRecentNoteSync, addImageSync, addNoteImageSync, - addAttributeSync, + addLabelSync, addApiTokenSync, addEntitySync, cleanupSyncRowsForMissingEntities, diff --git a/src/services/sync_update.js b/src/services/sync_update.js index fca910a97..0507434e4 100644 --- a/src/services/sync_update.js +++ b/src/services/sync_update.js @@ -130,17 +130,17 @@ async function updateNoteImage(entity, sourceId) { } } -async function updateAttribute(entity, sourceId) { - const origAttribute = await sql.getRow("SELECT * FROM attributes WHERE attributeId = ?", [entity.attributeId]); +async function updateLabel(entity, sourceId) { + const origLabel = await sql.getRow("SELECT * FROM labels WHERE labelId = ?", [entity.labelId]); - if (!origAttribute || origAttribute.dateModified <= entity.dateModified) { + if (!origLabel || origLabel.dateModified <= entity.dateModified) { await sql.doInTransaction(async () => { - await sql.replace("attributes", entity); + await sql.replace("labels", entity); - await sync_table.addAttributeSync(entity.attributeId, sourceId); + await sync_table.addLabelSync(entity.labelId, sourceId); }); - log.info("Update/sync attribute " + entity.attributeId); + log.info("Update/sync label " + entity.labelId); } } @@ -167,6 +167,6 @@ module.exports = { updateRecentNotes, updateImage, updateNoteImage, - updateAttribute, + updateLabel, updateApiToken }; \ No newline at end of file diff --git a/src/services/utils.js b/src/services/utils.js index b846737dd..cdb4b75c9 100644 --- a/src/services/utils.js +++ b/src/services/utils.js @@ -24,7 +24,7 @@ function newNoteImageId() { return randomString(12); } -function newAttributeId() { +function newLabelId() { return randomString(12); } @@ -159,7 +159,7 @@ module.exports = { newNoteRevisionId, newImageId, newNoteImageId, - newAttributeId, + newLabelId, newApiTokenId, toBase64, fromBase64, diff --git a/src/views/index.ejs b/src/views/index.ejs index d139d973c..cedc2ed11 100644 --- a/src/views/index.ejs +++ b/src/views/index.ejs @@ -58,7 +58,7 @@
@@ -204,10 +204,10 @@ -