release: v2.11.2
This commit is contained in:
@@ -1,10 +1,17 @@
|
||||
const DEFAULT_ACCOUNT_EMAIL_TEMPLATES = {
|
||||
verificationSubject: 'Verify your Pulse Signage email address',
|
||||
verificationBody: 'Hello [[display_name]]!\n\nConfirm this email address:\n\n[[action_button]]\n\nThis link expires in 30 minutes.',
|
||||
verificationBody: 'Hello [b][[display_name]][/b]!\n[[action_button]]\nThis [u]link[/u] expires in [i][[expiry_time]][/i].\nConfirm this email address: [[url]]',
|
||||
resetSubject: 'Reset your Pulse Signage password',
|
||||
resetBody: 'Hello [[display_name]]!\n\nUse this link to choose a new password:\n\n[[action_button]]\n\nThis link expires in 30 minutes.'
|
||||
resetBody: 'Hello [b][[display_name]][/b]!\n[[action_button]]\nThis [u]link[/u] expires in [i][[expiry_time]][/i].\nChoose a new password: [[url]]'
|
||||
};
|
||||
|
||||
function formatAccountEmailExpiry(value, unit) {
|
||||
const amount = Number(value);
|
||||
const normalizedAmount = Number.isFinite(amount) && amount > 0 ? Math.round(amount) : 30;
|
||||
const normalizedUnit = unit === 'hours' ? 'hour' : 'minute';
|
||||
return normalizedAmount + ' ' + normalizedUnit + (normalizedAmount === 1 ? '' : 's');
|
||||
}
|
||||
|
||||
function renderAccountEmailTemplate(subject, body, variables) {
|
||||
const values = variables || {};
|
||||
const replaceVariables = function (value) {
|
||||
@@ -22,10 +29,11 @@ function renderAccountEmailTemplate(subject, body, variables) {
|
||||
const actionButton = actionUrl ? '<a href="' + actionUrl + '" style="display:inline-block;background:#111827;color:#ffffff;padding:12px 22px;text-decoration:none;border-radius:4px;font-weight:600;">' + actionLabel + '</a>' : '';
|
||||
const actionBlock = actionButton ? '<div style="margin:24px 0;text-align:' + actionAlignment + ';">' + actionButton + '</div>' : '';
|
||||
const plainLink = actionUrl ? '<a href="' + actionUrl + '">' + actionUrl + '</a>' : '';
|
||||
const escapedBodyUrl = values.url ? String(values.url).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') : '';
|
||||
const hasButtonPlaceholder = escapedBody.indexOf('[[action_button]]') !== -1;
|
||||
const hasUrlPlaceholder = String(body || '').indexOf('[[url]]') !== -1;
|
||||
const bodyWithAction = hasButtonPlaceholder
|
||||
? escapedBody.replace(/\[\[action_button\]\]/g, actionBlock)
|
||||
? escapedBody.split(escapedBodyUrl).join(plainLink).replace(/\[\[action_button\]\]/g, actionBlock).replace(/\[\[action_link\]\]/g, plainLink)
|
||||
: hasUrlPlaceholder
|
||||
? escapedBody.split(actionUrl).join(actionBlock + plainLink)
|
||||
: escapedBody.replace(/\[\[action_link\]\]/g, plainLink).replace(actionUrl, plainLink);
|
||||
@@ -43,4 +51,4 @@ function renderAccountEmailTemplate(subject, body, variables) {
|
||||
return { subject: renderedSubject, text: plainText, html: html };
|
||||
}
|
||||
|
||||
module.exports = { DEFAULT_ACCOUNT_EMAIL_TEMPLATES, renderAccountEmailTemplate };
|
||||
module.exports = { DEFAULT_ACCOUNT_EMAIL_TEMPLATES, formatAccountEmailExpiry, renderAccountEmailTemplate };
|
||||
|
||||
@@ -30,16 +30,19 @@ const SETTING_DEFINITIONS = [
|
||||
{ key: 'email.smtp_password', type: 'string', defaultValue: '' },
|
||||
{ key: 'email.from_address', type: 'string', defaultValue: '' },
|
||||
{ key: 'email.from_name', type: 'string', defaultValue: 'Pulse Signage' },
|
||||
{ key: 'email.verification_expiry_minutes', type: 'integer', min: 1, defaultValue: 30 },
|
||||
{ key: 'email.reset_expiry_minutes', type: 'integer', min: 1, defaultValue: 30 },
|
||||
{ key: 'email.invitation_expiry_hours', type: 'integer', min: 1, defaultValue: 24 },
|
||||
{ key: 'email.verification_subject', type: 'string', defaultValue: 'Verify your Pulse Signage email address' },
|
||||
{ key: 'email.verification_body', type: 'string', defaultValue: 'Hello [[display_name]]!\n\nConfirm this email address:\n\n[[action_button]]\n\nThis link expires in 30 minutes.' },
|
||||
{ key: 'email.verification_body', type: 'string', defaultValue: 'Hello [b][[display_name]][/b]!\n[[action_button]]\nThis [u]link[/u] expires in [i][[expiry_time]][/i].\nConfirm this email address: [[url]]' },
|
||||
{ key: 'email.reset_subject', type: 'string', defaultValue: 'Reset your Pulse Signage password' },
|
||||
{ key: 'email.reset_body', type: 'string', defaultValue: 'Hello [[display_name]]!\n\nUse this link to choose a new password:\n\n[[action_button]]\n\nThis link expires in 30 minutes.' },
|
||||
{ key: 'email.reset_body', type: 'string', defaultValue: 'Hello [b][[display_name]][/b]!\n[[action_button]]\nThis [u]link[/u] expires in [i][[expiry_time]][/i].\nChoose a new password: [[url]]' },
|
||||
{ key: 'email.verification_button_alignment', type: 'enum', values: ['left', 'center', 'right'], defaultValue: 'center' },
|
||||
{ key: 'email.verification_button_text', type: 'string', defaultValue: 'Verify email address' },
|
||||
{ key: 'email.reset_button_alignment', type: 'enum', values: ['left', 'center', 'right'], defaultValue: 'center' },
|
||||
{ key: 'email.reset_button_text', type: 'string', defaultValue: 'Reset password' },
|
||||
{ key: 'email.invitation_subject', type: 'string', defaultValue: 'You have been invited to Pulse Signage' },
|
||||
{ key: 'email.invitation_body', type: 'string', defaultValue: 'Hello [[display_name]]!\n\nYou have been invited to create a Pulse Signage account.\n\n[[action_button]]\n\nThis invitation expires in 24 hours.' },
|
||||
{ key: 'email.invitation_body', type: 'string', defaultValue: 'Hello [b][[display_name]][/b]!\n[[action_button]]\nThis [u]invitation link[/u] expires in [i][[expiry_time]][/i].\nCreate your account: [[url]]' },
|
||||
{ key: 'email.invitation_button_alignment', type: 'enum', values: ['left', 'center', 'right'], defaultValue: 'center' },
|
||||
{ key: 'email.invitation_button_text', type: 'string', defaultValue: 'Accept invitation' },
|
||||
{ key: 'audit.enabled', type: 'boolean', defaultValue: true },
|
||||
|
||||
@@ -50,6 +50,27 @@ const SCREEN_CONTROL_COMMAND_LABELS = Object.freeze({
|
||||
});
|
||||
const { fetchAppSettings } = require('./app-settings');
|
||||
|
||||
function formatUserAgentLabel(userAgent) {
|
||||
const value = String(userAgent || '').trim();
|
||||
if (!value) return '';
|
||||
const browserMatch = value.match(/(?:Edg|OPR|Chrome|Firefox|Version|Electron)\/([\d.]+)/i);
|
||||
let browser = '';
|
||||
if (/Edg\//i.test(value)) browser = 'Edge';
|
||||
else if (/OPR\//i.test(value)) browser = 'Opera';
|
||||
else if (/Electron\//i.test(value)) browser = 'Electron';
|
||||
else if (/Chrome\//i.test(value)) browser = 'Chrome';
|
||||
else if (/Firefox\//i.test(value)) browser = 'Firefox';
|
||||
else if (/Version\/.*Safari\//i.test(value)) browser = 'Safari';
|
||||
const browserLabel = browserMatch && browser ? browser + ' ' + browserMatch[1] : browser;
|
||||
let operatingSystem = '';
|
||||
if (/Windows NT/i.test(value)) operatingSystem = 'Windows';
|
||||
else if (/Macintosh|Mac OS X/i.test(value)) operatingSystem = 'macOS';
|
||||
else if (/Android/i.test(value)) operatingSystem = 'Android';
|
||||
else if (/iPhone|iPad|iPod/i.test(value)) operatingSystem = 'iOS';
|
||||
else if (/Linux/i.test(value)) operatingSystem = 'Linux';
|
||||
return [browserLabel, operatingSystem].filter(Boolean).join(' on ') || value;
|
||||
}
|
||||
|
||||
function normalizeDetails(details) {
|
||||
if (details === undefined || details === null) {
|
||||
return null;
|
||||
@@ -136,6 +157,7 @@ module.exports = {
|
||||
AUDIT_CATEGORY_LABELS,
|
||||
SCREEN_CONTROL_COMMAND_KEYS,
|
||||
SCREEN_CONTROL_COMMAND_LABELS,
|
||||
formatUserAgentLabel,
|
||||
getRequestMetadata,
|
||||
buildAuditChanges,
|
||||
recordAuditEvent,
|
||||
|
||||
Reference in New Issue
Block a user