remove date management by lang
This commit is contained in:
@@ -262,6 +262,98 @@ Notifications.filternoitif = (tribe, tpl, alias, channel = "push", topics = [])
|
|||||||
return ok;
|
return ok;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Language of a recipient: the language of its own app (persons.lg, stamped by
|
||||||
|
* Persons.managetopic on each topic subscription) wins over the language of
|
||||||
|
* whoever triggered the event, which is only a fallback. Anything outside
|
||||||
|
* fr/en is forced to "en", the same way checkHeaders forces xlang.
|
||||||
|
*/
|
||||||
|
Notifications.recipientlang = (person, fallback) =>
|
||||||
|
person && ["fr", "en"].includes(person.lg)
|
||||||
|
? person.lg
|
||||||
|
: ["fr", "en"].includes(fallback)
|
||||||
|
? fallback
|
||||||
|
: "en";
|
||||||
|
|
||||||
|
const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}/;
|
||||||
|
const localdate_re = /\{\{#localdate\}\}\s*([^\s}]+)\s*\{\{\/localdate\}\}/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A date is never formatted by the caller: it sends the raw value (ISO) and the
|
||||||
|
* template declares how to display it.
|
||||||
|
*
|
||||||
|
* seekertemplate_fr.js : tpl.dateformat = { bookdate: "DD/MM/YYYY" }
|
||||||
|
* template : ... le {{#localdate}}bookdate{{/localdate}} ...
|
||||||
|
*
|
||||||
|
* The text of the section is the NAME of the field to display, and it is not
|
||||||
|
* "{{bookdate}}": mustache hands the raw inner text of a lambda, it does not
|
||||||
|
* render it, so the value is read in the view (closure) and not through a
|
||||||
|
* render callback. tpl.dateformat gives the dayjs format of that field.
|
||||||
|
*
|
||||||
|
* Anything that is not an ISO date (missing, empty, ...) is returned untouched,
|
||||||
|
* which also prevents an "Invalid Date" from reaching a recipient.
|
||||||
|
*
|
||||||
|
* The value MUST be a factory (() => function (text)): mustache calls a function
|
||||||
|
* found in the data with no argument during the lookup
|
||||||
|
* (mustache.js Context.lookup), so a plain (text) => ... lambda is silently
|
||||||
|
* ignored and the section renders its own content.
|
||||||
|
*/
|
||||||
|
Notifications.localdate = (tpl, view) => () => (text) => {
|
||||||
|
const field = text.trim();
|
||||||
|
const raw = view ? view[field] : undefined;
|
||||||
|
const value = raw === undefined || raw === null ? "" : String(raw).trim();
|
||||||
|
const fmt = (tpl.dateformat || {})[field];
|
||||||
|
if (!fmt || !ISO_DATE_RE.test(value)) return value;
|
||||||
|
return dayjs(value).format(fmt);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single rendering point for every notification template (email subject/html/
|
||||||
|
* text and push notiftitle/notifdescription).
|
||||||
|
*
|
||||||
|
* Notifications.render(tpl, view, ["subject", "html", "text"])
|
||||||
|
*
|
||||||
|
* `keys` is explicit on purpose: a template without tpl.text must keep failing
|
||||||
|
* instead of silently producing a mail without its text part. `meta` is only
|
||||||
|
* used to make the warnings actionable ({ template, lg }).
|
||||||
|
*
|
||||||
|
* Warns on both directions of a mismatch between tpl.dateformat and the
|
||||||
|
* template: a declared format never used means the raw ISO date is displayed,
|
||||||
|
* and a {{#localdate}} without declaration means the same thing silently.
|
||||||
|
*/
|
||||||
|
Notifications.render = (tpl, view, keys, meta = {}) => {
|
||||||
|
view.localdate = Notifications.localdate(tpl, view);
|
||||||
|
const rendered = {};
|
||||||
|
try {
|
||||||
|
for (const k of keys) rendered[k] = Mustache.render(tpl[k], view);
|
||||||
|
} finally {
|
||||||
|
// view is the caller object (req.body, emailsto entry, push recipient) and
|
||||||
|
// is persisted as is in persons.notifications[]: do not leak the factory.
|
||||||
|
delete view.localdate;
|
||||||
|
}
|
||||||
|
const sources = ["subject", "html", "text", "notiftitle", "notifdescription"]
|
||||||
|
.map((k) => (typeof tpl[k] === "string" ? tpl[k] : ""))
|
||||||
|
.join("\n");
|
||||||
|
const used = new Set([...sources.matchAll(localdate_re)].map((m) => m[1]));
|
||||||
|
const declared = Object.keys(tpl.dateformat || {});
|
||||||
|
const where = `${meta.template || "?"}_${meta.lg || "?"}`;
|
||||||
|
for (const field of declared) {
|
||||||
|
if (!used.has(field))
|
||||||
|
console.warn(
|
||||||
|
currentmod,
|
||||||
|
`dateformat "${field}" declared but never used in ${where}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const field of used) {
|
||||||
|
if (!declared.includes(field))
|
||||||
|
console.warn(
|
||||||
|
currentmod,
|
||||||
|
`{{#localdate}}${field}} used without dateformat in ${where}, raw value displayed`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return rendered;
|
||||||
|
};
|
||||||
|
|
||||||
Notifications.manageemail = async (
|
Notifications.manageemail = async (
|
||||||
data,
|
data,
|
||||||
templatename,
|
templatename,
|
||||||
@@ -360,9 +452,13 @@ Notifications.manageemail = async (
|
|||||||
emailtosend.to = emailtosend.to.slice(0, -1);
|
emailtosend.to = emailtosend.to.slice(0, -1);
|
||||||
emailtosend.templatename = templatename;
|
emailtosend.templatename = templatename;
|
||||||
emailtosend.lg = lg;
|
emailtosend.lg = lg;
|
||||||
emailtosend.subject = Mustache.render(tplemail.subject, email);
|
Object.assign(
|
||||||
emailtosend.html = Mustache.render(tplemail.html, email);
|
emailtosend,
|
||||||
emailtosend.text = Mustache.render(tplemail.text, email);
|
Notifications.render(tplemail, email, ["subject", "html", "text"], {
|
||||||
|
template: templatename,
|
||||||
|
lg,
|
||||||
|
})
|
||||||
|
);
|
||||||
if (email.cc) {
|
if (email.cc) {
|
||||||
emailtosend.cc += emailtosend.cc && emailtosend.cc != "" ? "," : "";
|
emailtosend.cc += emailtosend.cc && emailtosend.cc != "" ? "," : "";
|
||||||
emailtosend.cc += email.cc.join(",");
|
emailtosend.cc += email.cc.join(",");
|
||||||
|
|||||||
@@ -264,9 +264,10 @@ Pagans.sendmailkey = (data) => {
|
|||||||
const maildata = {
|
const maildata = {
|
||||||
from: tplemail.sender,
|
from: tplemail.sender,
|
||||||
to: person.email,
|
to: person.email,
|
||||||
subject: Mustache.render(tplemail.subject, person),
|
...Notifications.render(tplemail, person, ["subject", "html", "text"], {
|
||||||
html: Mustache.render(tplemail.html, person),
|
template: "createidentity",
|
||||||
text: Mustache.render(tplemail.text, person),
|
lg: data.lg,
|
||||||
|
}),
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
filename: `${person.alias}_privatekey.txt`,
|
filename: `${person.alias}_privatekey.txt`,
|
||||||
|
|||||||
@@ -165,9 +165,10 @@ Pagans.sendmailkey = (data) => {
|
|||||||
const maildata = {
|
const maildata = {
|
||||||
from: tplemail.sender,
|
from: tplemail.sender,
|
||||||
to: person.email,
|
to: person.email,
|
||||||
subject: Mustache.render(tplemail.subject, person),
|
...Notifications.render(tplemail, person, ["subject", "html", "text"], {
|
||||||
html: Mustache.render(tplemail.html, person),
|
template: "createidentity",
|
||||||
text: Mustache.render(tplemail.text, person),
|
lg: data.lg,
|
||||||
|
}),
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
filename: `${person.alias}_privatekey.txt`,
|
filename: `${person.alias}_privatekey.txt`,
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ router.get("/checkemail/:email/:digit6", checkHeaders, async (req, res) => {
|
|||||||
},
|
},
|
||||||
"checkemail",
|
"checkemail",
|
||||||
"adminapi",
|
"adminapi",
|
||||||
req.session.header.xlang || "fr",
|
Notifications.recipientlang(null, req.session.header.xlang),
|
||||||
1,
|
1,
|
||||||
["anonymous"]
|
["anonymous"]
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user