You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

230 lines
6.5 KiB
JavaScript

'use strict';
const winston = require('winston');
const _ = require('lodash');
const Benchpress = require('benchpressjs');
const plugins = require('../plugins');
const groups = require('../groups');
const translator = require('../translator');
const db = require('../database');
const apiController = require('../controllers/api');
const meta = require('../meta');
const widgets = module.exports;
widgets.render = async function (uid, options) {
if (!options.template) {
throw new Error('[[error:invalid-data]]');
}
const data = await widgets.getWidgetDataForTemplates(['global', options.template]);
delete data.global.drafts;
const locations = _.uniq(Object.keys(data.global).concat(Object.keys(data[options.template])));
const widgetData = await Promise.all(locations.map(location => renderLocation(location, data, uid, options)));
const returnData = {};
locations.forEach((location, i) => {
if (Array.isArray(widgetData[i]) && widgetData[i].length) {
returnData[location] = widgetData[i].filter(Boolean);
}
});
return returnData;
};
async function renderLocation(location, data, uid, options) {
const widgetsAtLocation = (data[options.template][location] || []).concat(data.global[location] || []);
if (!widgetsAtLocation.length) {
return [];
}
const renderedWidgets = await Promise.all(widgetsAtLocation.map(widget => renderWidget(widget, uid, options)));
return renderedWidgets;
}
async function renderWidget(widget, uid, options) {
if (!widget || !widget.data || (!!widget.data['hide-mobile'] && options.req.useragent.isMobile)) {
return;
}
const isVisible = await widgets.checkVisibility(widget.data, uid);
if (!isVisible) {
return;
}
let config = options.res.locals.config || {};
if (options.res.locals.isAPI) {
config = await apiController.loadConfig(options.req);
}
const userLang = config.userLang || meta.config.defaultLang || 'en-GB';
const templateData = _.assign({ }, options.templateData, { config: config });
const data = await plugins.hooks.fire(`filter:widget.render:${widget.widget}`, {
uid: uid,
area: options,
templateData: templateData,
data: widget.data,
req: options.req,
res: options.res,
});
if (!data) {
return;
}
let { html } = data;
if (widget.data.container && widget.data.container.match('{body}')) {
html = await Benchpress.compileRender(widget.data.container, {
title: widget.data.title,
body: html,
template: data.templateData && data.templateData.template,
});
}
if (html) {
html = await translator.translate(html, userLang);
}
return { html };
}
widgets.checkVisibility = async function (data, uid) {
let isVisible = true;
let isHidden = false;
if (data.groups.length) {
isVisible = await groups.isMemberOfAny(uid, data.groups);
}
if (data.groupsHideFrom.length) {
isHidden = await groups.isMemberOfAny(uid, data.groupsHideFrom);
}
return isVisible && !isHidden;
};
widgets.getWidgetDataForTemplates = async function (templates) {
const keys = templates.map(tpl => `widgets:${tpl}`);
const data = await db.getObjects(keys);
const returnData = {};
templates.forEach((template, index) => {
returnData[template] = returnData[template] || {};
const templateWidgetData = data[index] || {};
const locations = Object.keys(templateWidgetData);
locations.forEach((location) => {
if (templateWidgetData && templateWidgetData[location]) {
try {
returnData[template][location] = parseWidgetData(templateWidgetData[location]);
} catch (err) {
winston.error(`can not parse widget data. template: ${template} location: ${location}`);
returnData[template][location] = [];
}
} else {
returnData[template][location] = [];
}
});
});
return returnData;
};
widgets.getArea = async function (template, location) {
const result = await db.getObjectField(`widgets:${template}`, location);
if (!result) {
return [];
}
return parseWidgetData(result);
};
function parseWidgetData(data) {
const widgets = JSON.parse(data);
widgets.forEach((widget) => {
if (widget) {
widget.data.groups = widget.data.groups || [];
if (widget.data.groups && !Array.isArray(widget.data.groups)) {
widget.data.groups = [widget.data.groups];
}
widget.data.groupsHideFrom = widget.data.groupsHideFrom || [];
if (widget.data.groupsHideFrom && !Array.isArray(widget.data.groupsHideFrom)) {
widget.data.groupsHideFrom = [widget.data.groupsHideFrom];
}
}
});
return widgets;
}
widgets.setArea = async function (area) {
if (!area.location || !area.template) {
throw new Error('Missing location and template data');
}
await db.setObjectField(`widgets:${area.template}`, area.location, JSON.stringify(area.widgets));
};
widgets.setAreas = async function (areas) {
const templates = {};
areas.forEach((area) => {
if (!area.location || !area.template) {
throw new Error('Missing location and template data');
}
templates[area.template] = templates[area.template] || {};
templates[area.template][area.location] = JSON.stringify(area.widgets);
});
await db.setObjectBulk(
Object.keys(templates).map(tpl => [`widgets:${tpl}`, templates[tpl]])
);
};
widgets.reset = async function () {
const defaultAreas = [
{ name: 'Draft Zone', template: 'global', location: 'header' },
{ name: 'Draft Zone', template: 'global', location: 'footer' },
{ name: 'Draft Zone', template: 'global', location: 'sidebar' },
];
const [areas, drafts] = await Promise.all([
plugins.hooks.fire('filter:widgets.getAreas', defaultAreas),
widgets.getArea('global', 'drafts'),
]);
let saveDrafts = drafts || [];
for (const area of areas) {
/* eslint-disable no-await-in-loop */
const areaData = await widgets.getArea(area.template, area.location);
saveDrafts = saveDrafts.concat(areaData);
area.widgets = [];
await widgets.setArea(area);
}
await widgets.setArea({
template: 'global',
location: 'drafts',
widgets: saveDrafts,
});
};
widgets.resetTemplate = async function (template) {
const area = await db.getObject(`widgets:${template}.tpl`);
const toBeDrafted = _.flatMap(Object.values(area), value => JSON.parse(value));
await db.delete(`widgets:${template}.tpl`);
let draftWidgets = await db.getObjectField('widgets:global', 'drafts');
draftWidgets = JSON.parse(draftWidgets).concat(toBeDrafted);
await db.setObjectField('widgets:global', 'drafts', JSON.stringify(draftWidgets));
};
widgets.resetTemplates = async function (templates) {
for (const template of templates) {
/* eslint-disable no-await-in-loop */
await widgets.resetTemplate(template);
}
};
require('../promisify')(widgets);