Add title to PDF metadata and improve PDF filename handling

Adds a title tag to the HTML output for PDF generation and refactors the Content-Disposition header to include both a stable ASCII filename and a localized, human-readable filename for PDF downloads.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 7b3c6ef0-56bb-48e7-8f67-40892dcf240d
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/4ugHzxo
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
riyadhafraa
2026-05-11 20:59:42 +00:00
parent 6dc016261c
commit 0489f6b1dc
2 changed files with 32 additions and 3 deletions
@@ -308,6 +308,7 @@ export function buildScheduleHtml(input: RenderPdfInput): string {
<html lang="${input.lang}" dir="${dir}">
<head>
<meta charset="utf-8"/>
<title>${esc(input.labels.title)}</title>
<style>
@font-face {
font-family: '${fontFaceName}';
@@ -3216,12 +3216,25 @@ router.get(
console.error("[executive-meetings] PDF archive insert failed", err);
}
const dayName = new Intl.DateTimeFormat(lang === "ar" ? "ar-SA" : "en-US", { weekday: "long" }).format(new Date(dateRaw + "T00:00:00"));
const pdfFileName = lang === "ar" ? `قائمة الاجتماعات يوم ${dayName}.pdf` : `meetings-${dayName}.pdf`;
// Two-part filename per RFC 5987:
// - `filename=` is a stable ASCII slug (`executive-meetings-YYYY-MM-DD.pdf`)
// so older clients, curl, and tests get a predictable, parseable name.
// - `filename*=UTF-8''…` is the localized human-readable name (Arabic
// "قائمة الاجتماعات يوم …" / English "Meetings - …") that modern
// browsers prefer when offering the download.
const dayName = new Intl.DateTimeFormat(
lang === "ar" ? "ar-SA" : "en-US",
{ weekday: "long" },
).format(new Date(dateRaw + "T00:00:00"));
const asciiFileName = `executive-meetings-${dateRaw}.pdf`;
const friendlyFileName =
lang === "ar"
? `قائمة الاجتماعات يوم ${dayName}.pdf`
: `Meetings - ${dayName}.pdf`;
res.setHeader("Content-Type", "application/pdf");
res.setHeader(
"Content-Disposition",
`attachment; filename="${encodeURIComponent(pdfFileName)}"; filename*=UTF-8''${encodeURIComponent(pdfFileName)}`,
`attachment; filename="${asciiFileName}"; filename*=UTF-8''${encodeURIComponent(friendlyFileName)}`,
);
res.setHeader("Content-Length", String(pdf.byteLength));
res.setHeader("Cache-Control", "no-store");
@@ -3353,6 +3366,21 @@ const upsertFontSettingsHandler = async (
res.status(403).json({ error: "Forbidden", code: "forbidden" });
return;
}
// Logo lives on the GLOBAL row only — a user-scope row is the per-user
// overlay (font/color/etc.) and never owns a logo. `null` is allowed
// (it just means "no override here", which is already the default for
// user rows). Anything else on a user-scope payload is rejected so
// we don't silently drop the field on the floor.
if (
data.scope === "user" &&
typeof data.logoObjectPath === "string"
) {
res.status(400).json({
error: "logoObjectPath is only allowed on global font settings",
code: "logo_user_scope_forbidden",
});
return;
}
const targetUserId = data.scope === "global" ? null : userId;
const logoSentByAdmin =
isAdmin && data.logoObjectPath !== undefined;