NodeBB/NodeBB

View on GitHub
src/navigation/admin.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict';

const validator = require('validator');
const winston = require('winston');

const plugins = require('../plugins');
const db = require('../database');
const pubsub = require('../pubsub');

const admin = module.exports;
let cache = null;

pubsub.on('admin:navigation:save', () => {
    cache = null;
});

admin.save = async function (data) {
    const order = Object.keys(data);
    const bulkSet = [];
    data.forEach((item, index) => {
        item.order = order[index];
        if (item.hasOwnProperty('groups')) {
            item.groups = JSON.stringify(item.groups);
        }
        bulkSet.push([`navigation:enabled:${item.order}`, item]);
    });

    cache = null;
    pubsub.publish('admin:navigation:save');
    const ids = await db.getSortedSetRange('navigation:enabled', 0, -1);
    await db.deleteAll(ids.map(id => `navigation:enabled:${id}`));
    await db.setObjectBulk(bulkSet);
    await db.delete('navigation:enabled');
    await db.sortedSetAdd('navigation:enabled', order, order);
};

admin.getAdmin = async function () {
    const [enabled, available] = await Promise.all([
        admin.get(),
        getAvailable(),
    ]);
    return { enabled: enabled, available: available };
};

const fieldsToEscape = ['iconClass', 'class', 'route', 'id', 'text', 'textClass', 'title'];

admin.escapeFields = navItems => toggleEscape(navItems, true);
admin.unescapeFields = navItems => toggleEscape(navItems, false);

function toggleEscape(navItems, flag) {
    navItems.forEach((item) => {
        if (item) {
            fieldsToEscape.forEach((field) => {
                if (item.hasOwnProperty(field)) {
                    item[field] = validator[flag ? 'escape' : 'unescape'](String(item[field]));
                }
            });
        }
    });
}

admin.get = async function () {
    if (cache) {
        return cache.map(item => ({ ...item }));
    }
    const ids = await db.getSortedSetRange('navigation:enabled', 0, -1);
    const data = await db.getObjects(ids.map(id => `navigation:enabled:${id}`));
    cache = data.filter(Boolean).map((item) => {
        if (item.hasOwnProperty('groups')) {
            try {
                item.groups = JSON.parse(item.groups);
            } catch (err) {
                winston.error(err.stack);
                item.groups = [];
            }
        }
        item.groups = item.groups || [];
        if (item.groups && !Array.isArray(item.groups)) {
            item.groups = [item.groups];
        }
        return item;
    });
    admin.escapeFields(cache);

    return cache.map(item => ({ ...item }));
};

async function getAvailable() {
    const core = require('../../install/data/navigation.json').map((item) => {
        item.core = true;
        item.id = item.id || '';
        return item;
    });

    const navItems = await plugins.hooks.fire('filter:navigation.available', core);
    navItems.forEach((item) => {
        if (item && !item.hasOwnProperty('enabled')) {
            item.enabled = true;
        }
    });
    return navItems;
}

require('../promisify')(admin);