Fix Outlook registration links
This commit is contained in:
1 parent
e170d57577
commit
15bc6f063d
5 files changed
+73
-16
No files matched your search
+13
-15
@@ -49,7 +49,9 @@ export function buildInvitationEmail({ rencontre, baseUrl, texts = {} }) {
|
||||
year: 'numeric',
|
||||
});
|
||||
const restantes = Math.max(0, r.places - (r.inscrits ?? 0));
|
||||
const inscriptionUrl = `${baseUrl}/?inscription=${r.id}`;
|
||||
// A dedicated route is more reliable from Outlook than a homepage query
|
||||
// string: redirects, tracking filters and cached homepages can drop it.
|
||||
const inscriptionUrl = `${baseUrl}/inscription/${r.id}`;
|
||||
const logoUrl = `${baseUrl}/assets/logo.jpg`;
|
||||
const subject = `Invitation — ${r.titre} · ${dateStr}`;
|
||||
|
||||
@@ -71,21 +73,17 @@ export function buildInvitationEmail({ rencontre, baseUrl, texts = {} }) {
|
||||
? `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="width:100%;border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;"><tr><td style="font-family:Arial,Helvetica,sans-serif;font-size:15px;line-height:25px;mso-line-height-rule:exactly;color:#5A544E;${extra}">${escML(text)}</td></tr></table>`
|
||||
: '';
|
||||
|
||||
// "Bulletproof" CTA button. Outlook (Word engine) ignores padding and
|
||||
// display:inline-block on <a>, collapsing the button to a plain link, so
|
||||
// it needs a VML roundrect. Other clients get the styled <a>, sized with
|
||||
// a fixed width + line-height (not padding) so both paths match. The
|
||||
// conditional comments are inert HTML comments in normal renderers.
|
||||
// A table-backed CTA survives rich-text paste into Outlook. In particular,
|
||||
// the visible label remains a normal <a href>, whereas VML hyperlinks are
|
||||
// commonly stripped when Outlook receives them through the clipboard.
|
||||
const ctaButton = (url, label) => `
|
||||
<!--[if mso]>
|
||||
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${esc(url)}" style="height:48px;v-text-anchor:middle;width:300px;" arcsize="7%" fillcolor="#C1272D" strokecolor="#C1272D">
|
||||
<w:anchorlock/>
|
||||
<center style="color:#FFFFFF;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;">${label}</center>
|
||||
</v:roundrect>
|
||||
<![endif]-->
|
||||
<!--[if !mso]><!-->
|
||||
<a href="${esc(url)}" target="_blank" style="display:inline-block;width:300px;line-height:48px;background-color:#C1272D;color:#FFFFFF;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;text-align:center;text-decoration:none;border-radius:3px;-webkit-text-size-adjust:none;">${label}</a>
|
||||
<!--<![endif]-->`;
|
||||
<table role="presentation" width="300" cellpadding="0" cellspacing="0" border="0" style="width:300px;border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td width="300" height="48" align="center" valign="middle" bgcolor="#C1272D" style="width:300px;height:48px;background-color:#C1272D;">
|
||||
<a href="${esc(url)}" target="_blank" title="Ouvrir l'inscription" style="display:block;width:300px;line-height:48px;color:#FFFFFF;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;text-align:center;text-decoration:none;-webkit-text-size-adjust:none;">${label}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>`;
|
||||
|
||||
const html = `<!doctype html>
|
||||
<html lang="fr" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
@@ -56,6 +56,26 @@ publicRouter.get('/bootstrap', async (_req, res, next) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Direct event lookup used by links from invitation emails. It deliberately
|
||||
// does not depend on the homepage bootstrap payload, so the registration page
|
||||
// can always resolve a valid upcoming event by id.
|
||||
publicRouter.get('/rencontres/:id', validate(idParam, 'params'), async (req, res, next) => {
|
||||
try {
|
||||
const result = await query(
|
||||
`SELECT r.id, r.titre, r.date_renc, r.heure, r.lieu, r.description, r.places,
|
||||
r.participants_par_compte, r.image_path, COUNT(i.id)::int AS inscrits
|
||||
FROM rencontres r LEFT JOIN inscriptions i ON i.rencontre_id = r.id
|
||||
WHERE r.id = $1 AND r.date_renc >= CURRENT_DATE
|
||||
GROUP BY r.id`,
|
||||
[req.params.id]
|
||||
);
|
||||
if (result.rowCount === 0) return res.status(404).json({ error: 'Rencontre introuvable' });
|
||||
res.json({ rencontre: result.rows[0] });
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// Membership request (home page form)
|
||||
publicRouter.post(
|
||||
'/demandes-adhesion',
|
||||
|
||||
Reference in new issue
Block a user