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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user