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.
226 lines
6.5 KiB
JavaScript
226 lines
6.5 KiB
JavaScript
'use strict';
|
|
|
|
const winston = require('winston');
|
|
const _ = require('lodash');
|
|
const Benchpress = require('benchpressjs');
|
|
const util = require('util');
|
|
|
|
const plugins = require('../plugins');
|
|
const groups = require('../groups');
|
|
const translator = require('../translator');
|
|
const db = require('../database');
|
|
const apiController = require('../controllers/api');
|
|
const loadConfigAsync = util.promisify(apiController.loadConfig);
|
|
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(function (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 checkVisibility(widget, uid);
|
|
if (!isVisible) {
|
|
return;
|
|
}
|
|
|
|
let config = options.res.locals.config || {};
|
|
if (options.res.locals.isAPI) {
|
|
config = await loadConfigAsync(options.req);
|
|
}
|
|
|
|
const userLang = config.userLang || meta.config.defaultLang || 'en-GB';
|
|
const templateData = _.assign({ }, options.templateData, { config: config });
|
|
const data = await plugins.fireHook('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 (typeof html !== 'string') {
|
|
html = data.html;
|
|
} else {
|
|
winston.warn('[widgets.render] passing a string is deprecated!, filter:widget.render:' + widget.widget + '. Please set hookData.html in your plugin.');
|
|
}
|
|
|
|
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 !== undefined) {
|
|
html = await translator.translate(html, userLang);
|
|
}
|
|
|
|
return { html: html };
|
|
}
|
|
|
|
async function checkVisibility(widget, uid) {
|
|
let isVisible = true;
|
|
let isHidden = false;
|
|
if (widget.data.groups.length) {
|
|
isVisible = await groups.isMemberOfAny(uid, widget.data.groups);
|
|
}
|
|
if (widget.data.groupsHideFrom.length) {
|
|
isHidden = await groups.isMemberOfAny(uid, widget.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(function (template, index) {
|
|
returnData[template] = returnData[template] || {};
|
|
|
|
const templateWidgetData = data[index] || {};
|
|
const locations = Object.keys(templateWidgetData);
|
|
|
|
locations.forEach(function (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(function (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.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.fireHook('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) {
|
|
let toBeDrafted = [];
|
|
const area = await db.getObject('widgets:' + template + '.tpl');
|
|
for (var location in area) {
|
|
if (area.hasOwnProperty(location)) {
|
|
toBeDrafted = toBeDrafted.concat(JSON.parse(area[location]));
|
|
}
|
|
}
|
|
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);
|