mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 03:46:37 +01:00 
			
		
		
		
	renamed outstanding attribute references to labels
This commit is contained in:
		@@ -58,8 +58,8 @@ class Note extends Entity {
 | 
			
		||||
    async getLabelMap() {
 | 
			
		||||
        const map = {};
 | 
			
		||||
 | 
			
		||||
        for (const attr of await this.getLabels()) {
 | 
			
		||||
            map[attr.name] = attr.value;
 | 
			
		||||
        for (const label of await this.getLabels()) {
 | 
			
		||||
            map[label.name] = label.value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return map;
 | 
			
		||||
 
 | 
			
		||||
@@ -38,29 +38,29 @@ function LabelsModel() {
 | 
			
		||||
                // 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 attr = self.getTargetLabel(this);
 | 
			
		||||
                    const label = self.getTargetLabel(this);
 | 
			
		||||
 | 
			
		||||
                    attr().position = position++;
 | 
			
		||||
                    label().position = position++;
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.deleteLabel = function(data, event) {
 | 
			
		||||
        const attr = self.getTargetLabel(event.target);
 | 
			
		||||
        const attrData = attr();
 | 
			
		||||
        const label = self.getTargetLabel(event.target);
 | 
			
		||||
        const labelData = label();
 | 
			
		||||
 | 
			
		||||
        if (attrData) {
 | 
			
		||||
            attrData.isDeleted = 1;
 | 
			
		||||
        if (labelData) {
 | 
			
		||||
            labelData.isDeleted = 1;
 | 
			
		||||
 | 
			
		||||
            attr(attrData);
 | 
			
		||||
            label(labelData);
 | 
			
		||||
 | 
			
		||||
            addLastEmptyRow();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function isValid() {
 | 
			
		||||
        for (let attrs = self.labels(), i = 0; i < attrs.length; i++) {
 | 
			
		||||
        for (let labels = self.labels(), i = 0; i < labels.length; i++) {
 | 
			
		||||
            if (self.isEmptyName(i)) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
@@ -83,8 +83,8 @@ function LabelsModel() {
 | 
			
		||||
        const noteId = noteDetailService.getCurrentNoteId();
 | 
			
		||||
 | 
			
		||||
        const labelsToSave = self.labels()
 | 
			
		||||
            .map(attr => attr())
 | 
			
		||||
            .filter(attr => attr.labelId !== "" || attr.name !== "");
 | 
			
		||||
            .map(label => label())
 | 
			
		||||
            .filter(label => label.labelId !== "" || label.name !== "");
 | 
			
		||||
 | 
			
		||||
        const labels = await server.put('notes/' + noteId + '/labels', labelsToSave);
 | 
			
		||||
 | 
			
		||||
@@ -98,8 +98,8 @@ function LabelsModel() {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function addLastEmptyRow() {
 | 
			
		||||
        const attrs = self.labels().filter(attr => attr().isDeleted === 0);
 | 
			
		||||
        const last = attrs.length === 0 ? null : attrs[attrs.length - 1]();
 | 
			
		||||
        const labels = self.labels().filter(attr => attr().isDeleted === 0);
 | 
			
		||||
        const last = labels.length === 0 ? null : labels[labels.length - 1]();
 | 
			
		||||
 | 
			
		||||
        if (!last || last.name.trim() !== "" || last.value !== "") {
 | 
			
		||||
            self.labels.push(ko.observable({
 | 
			
		||||
@@ -115,9 +115,9 @@ function LabelsModel() {
 | 
			
		||||
    this.labelChanged = function (data, event) {
 | 
			
		||||
        addLastEmptyRow();
 | 
			
		||||
 | 
			
		||||
        const attr = self.getTargetLabel(event.target);
 | 
			
		||||
        const label = self.getTargetLabel(event.target);
 | 
			
		||||
 | 
			
		||||
        attr.valueHasMutated();
 | 
			
		||||
        label.valueHasMutated();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.isNotUnique = function(index) {
 | 
			
		||||
@@ -127,10 +127,10 @@ function LabelsModel() {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let attrs = self.labels(), i = 0; i < attrs.length; i++) {
 | 
			
		||||
            const attr = attrs[i]();
 | 
			
		||||
        for (let labels = self.labels(), i = 0; i < labels.length; i++) {
 | 
			
		||||
            const label = labels[i]();
 | 
			
		||||
 | 
			
		||||
            if (index !== i && cur.name === attr.name) {
 | 
			
		||||
            if (index !== i && cur.name === label.name) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -171,10 +171,10 @@ $(document).on('focus', '.label-name', function (e) {
 | 
			
		||||
        $(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(attr => {
 | 
			
		||||
            source: labelNames.map(label => {
 | 
			
		||||
                return {
 | 
			
		||||
                    label: attr,
 | 
			
		||||
                    value: attr
 | 
			
		||||
                    label: label,
 | 
			
		||||
                    value: label
 | 
			
		||||
                }
 | 
			
		||||
            }),
 | 
			
		||||
            minLength: 0
 | 
			
		||||
@@ -201,10 +201,10 @@ $(document).on('focus', '.label-value', async function (e) {
 | 
			
		||||
        $(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(attr => {
 | 
			
		||||
            source: labelValues.map(label => {
 | 
			
		||||
                return {
 | 
			
		||||
                    label: attr,
 | 
			
		||||
                    value: attr
 | 
			
		||||
                    label: label,
 | 
			
		||||
                    value: label
 | 
			
		||||
                }
 | 
			
		||||
            }),
 | 
			
		||||
            minLength: 0
 | 
			
		||||
 
 | 
			
		||||
@@ -79,11 +79,11 @@ function formatValueWithWhitespace(val) {
 | 
			
		||||
    return /[^\w_-]/.test(val) ? '"' + val + '"' : val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function formatLabel(attr) {
 | 
			
		||||
    let str = "@" + formatValueWithWhitespace(attr.name);
 | 
			
		||||
function formatLabel(label) {
 | 
			
		||||
    let str = "@" + formatValueWithWhitespace(label.name);
 | 
			
		||||
 | 
			
		||||
    if (attr.value !== "") {
 | 
			
		||||
        str += "=" + formatValueWithWhitespace(attr.value);
 | 
			
		||||
    if (label.value !== "") {
 | 
			
		||||
        str += "=" + formatValueWithWhitespace(label.value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return str;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ async function exportNoteInner(branchId, directory, pack) {
 | 
			
		||||
 | 
			
		||||
    const metadata = await getMetadata(note);
 | 
			
		||||
 | 
			
		||||
    if (metadata.labels.find(attr => attr.name === 'exclude_from_export')) {
 | 
			
		||||
    if (metadata.labels.find(label => label.name === 'exclude_from_export')) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -63,10 +63,10 @@ async function getMetadata(note) {
 | 
			
		||||
        title: note.title,
 | 
			
		||||
        type: note.type,
 | 
			
		||||
        mime: note.mime,
 | 
			
		||||
        labels: (await note.getLabels()).map(attr => {
 | 
			
		||||
        labels: (await note.getLabels()).map(label => {
 | 
			
		||||
            return {
 | 
			
		||||
                name: attr.name,
 | 
			
		||||
                value: attr.value
 | 
			
		||||
                name: label.name,
 | 
			
		||||
                value: label.value
 | 
			
		||||
            };
 | 
			
		||||
        })
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -115,8 +115,8 @@ async function importNotes(files, parentNoteId) {
 | 
			
		||||
            mime: file.meta.mime
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        for (const attr of file.meta.labels) {
 | 
			
		||||
            await labels.createLabel(noteId, attr.name, attr.value);
 | 
			
		||||
        for (const label of file.meta.labels) {
 | 
			
		||||
            await labels.createLabel(noteId, label.name, label.value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (file.children.length > 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,32 +16,32 @@ async function updateNoteLabels(req, res, next) {
 | 
			
		||||
    const labels = req.body;
 | 
			
		||||
    const now = utils.nowDate();
 | 
			
		||||
 | 
			
		||||
    for (const attr of labels) {
 | 
			
		||||
        if (attr.labelId) {
 | 
			
		||||
    for (const label of labels) {
 | 
			
		||||
        if (label.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]);
 | 
			
		||||
                [label.name, label.value, now, label.isDeleted, label.position, label.labelId]);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // if it was "created" and then immediatelly deleted, we just don't create it at all
 | 
			
		||||
            if (attr.isDeleted) {
 | 
			
		||||
            if (label.isDeleted) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            attr.labelId = utils.newLabelId();
 | 
			
		||||
            label.labelId = utils.newLabelId();
 | 
			
		||||
 | 
			
		||||
            await sql.insert("labels", {
 | 
			
		||||
                labelId: attr.labelId,
 | 
			
		||||
                labelId: label.labelId,
 | 
			
		||||
                noteId: noteId,
 | 
			
		||||
                name: attr.name,
 | 
			
		||||
                value: attr.value,
 | 
			
		||||
                position: attr.position,
 | 
			
		||||
                name: label.name,
 | 
			
		||||
                value: label.value,
 | 
			
		||||
                position: label.position,
 | 
			
		||||
                dateCreated: now,
 | 
			
		||||
                dateModified: now,
 | 
			
		||||
                isDeleted: false
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await sync_table.addLabelSync(attr.labelId);
 | 
			
		||||
        await sync_table.addLabelSync(label.labelId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return await sql.getRows("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]);
 | 
			
		||||
@@ -50,9 +50,9 @@ async function updateNoteLabels(req, res, next) {
 | 
			
		||||
async function getAllLabelNames(req) {
 | 
			
		||||
    const names = await sql.getColumn("SELECT DISTINCT name FROM labels WHERE isDeleted = 0");
 | 
			
		||||
 | 
			
		||||
    for (const attr of labels.BUILTIN_LABELS) {
 | 
			
		||||
        if (!names.includes(attr)) {
 | 
			
		||||
            names.push(attr);
 | 
			
		||||
    for (const label of labels.BUILTIN_LABELS) {
 | 
			
		||||
        if (!names.includes(label)) {
 | 
			
		||||
            names.push(label);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,9 @@ const parseFilters = require('../../services/parse_filters');
 | 
			
		||||
const buildSearchQuery = require('../../services/build_search_query');
 | 
			
		||||
 | 
			
		||||
async function searchNotes(req) {
 | 
			
		||||
    const {attrFilters, searchText} = parseFilters(req.params.searchString);
 | 
			
		||||
    const {labelFilters, searchText} = parseFilters(req.params.searchString);
 | 
			
		||||
 | 
			
		||||
    const {query, params} = buildSearchQuery(attrFilters, searchText);
 | 
			
		||||
    const {query, params} = buildSearchQuery(labelFilters, searchText);
 | 
			
		||||
 | 
			
		||||
    const noteIds = await sql.getColumn(query, params);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,9 +34,7 @@ async function backupNow() {
 | 
			
		||||
 | 
			
		||||
        log.info("Created backup at " + backupFile);
 | 
			
		||||
 | 
			
		||||
        await sql.doInTransaction(async () => {
 | 
			
		||||
            await options.setOption('last_backup_date', now);
 | 
			
		||||
        });
 | 
			
		||||
        await options.setOption('last_backup_date', now);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
module.exports = function(attrFilters, searchText) {
 | 
			
		||||
module.exports = function(labelFilters, searchText) {
 | 
			
		||||
    const joins = [];
 | 
			
		||||
    const joinParams = [];
 | 
			
		||||
    let where = '1';
 | 
			
		||||
@@ -6,31 +6,31 @@ module.exports = function(attrFilters, searchText) {
 | 
			
		||||
 | 
			
		||||
    let i = 1;
 | 
			
		||||
 | 
			
		||||
    for (const filter of attrFilters) {
 | 
			
		||||
        joins.push(`LEFT JOIN labels AS attr${i} ON attr${i}.noteId = notes.noteId AND attr${i}.name = ?`);
 | 
			
		||||
    for (const filter of labelFilters) {
 | 
			
		||||
        joins.push(`LEFT JOIN labels AS label${i} ON label${i}.noteId = notes.noteId AND label${i}.name = ?`);
 | 
			
		||||
        joinParams.push(filter.name);
 | 
			
		||||
 | 
			
		||||
        where += " " + filter.relation + " ";
 | 
			
		||||
 | 
			
		||||
        if (filter.operator === 'exists') {
 | 
			
		||||
            where += `attr${i}.labelId IS NOT NULL`;
 | 
			
		||||
            where += `label${i}.labelId IS NOT NULL`;
 | 
			
		||||
        }
 | 
			
		||||
        else if (filter.operator === 'not-exists') {
 | 
			
		||||
            where += `attr${i}.labelId IS NULL`;
 | 
			
		||||
            where += `label${i}.labelId IS NULL`;
 | 
			
		||||
        }
 | 
			
		||||
        else if (filter.operator === '=' || filter.operator === '!=') {
 | 
			
		||||
            where += `attr${i}.value ${filter.operator} ?`;
 | 
			
		||||
            where += `label${i}.value ${filter.operator} ?`;
 | 
			
		||||
            whereParams.push(filter.value);
 | 
			
		||||
        }
 | 
			
		||||
        else if ([">", ">=", "<", "<="].includes(filter.operator)) {
 | 
			
		||||
            const floatParam = parseFloat(filter.value);
 | 
			
		||||
 | 
			
		||||
            if (isNaN(floatParam)) {
 | 
			
		||||
                where += `attr${i}.value ${filter.operator} ?`;
 | 
			
		||||
                where += `label${i}.value ${filter.operator} ?`;
 | 
			
		||||
                whereParams.push(filter.value);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                where += `CAST(attr${i}.value AS DECIMAL) ${filter.operator} ?`;
 | 
			
		||||
                where += `CAST(label${i}.value AS DECIMAL) ${filter.operator} ?`;
 | 
			
		||||
                whereParams.push(floatParam);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -104,8 +104,8 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
 | 
			
		||||
    const {noteId} = await createNewNote(parentNoteId, note);
 | 
			
		||||
 | 
			
		||||
    if (extraOptions.labels) {
 | 
			
		||||
        for (const attrName in extraOptions.labels) {
 | 
			
		||||
            await labels.createLabel(noteId, attrName, extraOptions.labels[attrName]);
 | 
			
		||||
        for (const labelName in extraOptions.labels) {
 | 
			
		||||
            await labels.createLabel(noteId, labelName, extraOptions.labels[labelName]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
module.exports = function(searchText) {
 | 
			
		||||
    const attrFilters = [];
 | 
			
		||||
    const labelFilters = [];
 | 
			
		||||
 | 
			
		||||
    const attrRegex = /(\b(and|or)\s+)?@(!?)([\w_-]+|"[^"]+")((=|!=|<|<=|>|>=)([\w_-]+|"[^"]+"))?/i;
 | 
			
		||||
    const labelRegex = /(\b(and|or)\s+)?@(!?)([\w_-]+|"[^"]+")((=|!=|<|<=|>|>=)([\w_-]+|"[^"]+"))?/i;
 | 
			
		||||
 | 
			
		||||
    let match = attrRegex.exec(searchText);
 | 
			
		||||
    let match = labelRegex.exec(searchText);
 | 
			
		||||
 | 
			
		||||
    function trimQuotes(str) { return str.startsWith('"') ? str.substr(1, str.length - 2) : str; }
 | 
			
		||||
 | 
			
		||||
@@ -11,7 +11,7 @@ module.exports = function(searchText) {
 | 
			
		||||
        const relation = match[2] !== undefined ? match[2].toLowerCase() : 'and';
 | 
			
		||||
        const operator = match[3] === '!' ? 'not-exists' : 'exists';
 | 
			
		||||
 | 
			
		||||
        attrFilters.push({
 | 
			
		||||
        labelFilters.push({
 | 
			
		||||
            relation: relation,
 | 
			
		||||
            name: trimQuotes(match[4]),
 | 
			
		||||
            operator: match[6] !== undefined ? match[6] : operator,
 | 
			
		||||
@@ -21,8 +21,8 @@ module.exports = function(searchText) {
 | 
			
		||||
        // remove labels from further fulltext search
 | 
			
		||||
        searchText = searchText.split(match[0]).join('');
 | 
			
		||||
 | 
			
		||||
        match = attrRegex.exec(searchText);
 | 
			
		||||
        match = labelRegex.exec(searchText);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {attrFilters, searchText};
 | 
			
		||||
    return {labelFilters: labelFilters, searchText};
 | 
			
		||||
};
 | 
			
		||||
@@ -44,12 +44,12 @@ function ScriptApi(startNote, currentNote) {
 | 
			
		||||
        return await repository.getNote(noteId);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.getNotesWithLabel = async function (attrName, attrValue) {
 | 
			
		||||
        return await labels.getNotesWithLabel(attrName, attrValue);
 | 
			
		||||
    this.getNotesWithLabel = async function (labelName, labelValue) {
 | 
			
		||||
        return await labels.getNotesWithLabel(labelName, labelValue);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.getNoteWithLabel = async function (attrName, attrValue) {
 | 
			
		||||
        const notes = await this.getNotesWithLabel(attrName, attrValue);
 | 
			
		||||
    this.getNoteWithLabel = async function (labelName, labelValue) {
 | 
			
		||||
        const notes = await this.getNotesWithLabel(labelName, labelValue);
 | 
			
		||||
 | 
			
		||||
        return notes.length > 0 ? notes[0] : null;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user