Staff report: pass Civi-signed file URLs through to clicks
Civi serves uploaded files at /civicrm/file?id=X&eid=Y&fcs=<JWT>; the fcs
is a JWT signed with the site key. Without it, Civi's file handler crashes
on a null JWT decode (Firebase\JWT\JWT::decode argument null). We don't
have the site key on the Next.js side, so let Civi mint the URLs for us.
Backend (/api/staff/report):
- Add file_name selects for org-side file fields (Certificate of
Incorporation and friends) so org files have names alongside URLs.
- Collect every file id referenced by activity and org custom fields.
- Call APIv4 Attachment.get with select: ["id", "url"] to fetch signed
URLs in one round trip. Build a urlByFileId map.
- Org-side file values are now wrapped in { id, file_name, url } shape
matching the activity-side files (previously bare file ids that the
frontend couldn't render).
- Activity-side file values gain a url property from the map.
- If Attachment.get doesn't expose url on this Civi version, the call is
caught and we fall through to bare URLs without fcs (no regression).
Frontend (FieldValue):
- Prefer v.url when present, normalizing absolute and relative shapes
against CIVI_BASE_URL.
- Fall back to /civicrm/file?reset=1&id=X if url wasn't provided.
This commit is contained in:
@@ -453,16 +453,24 @@ function FieldValue({
|
||||
civiBaseUrl: string;
|
||||
}) {
|
||||
if (field.descriptor.render === "file") {
|
||||
const v = entry.value as { id?: number | string; file_name?: string } | null;
|
||||
const v = entry.value as
|
||||
| { id?: number | string; file_name?: string; url?: string }
|
||||
| null;
|
||||
if (!v || v.id === undefined) return <span>—</span>;
|
||||
const id = String(v.id);
|
||||
const name = v.file_name ?? `file-${id}`;
|
||||
// Civi serves uploaded files at /civicrm/file?reset=1&id=<id>.
|
||||
// The staff member is already authenticated to Civi (they came from
|
||||
// there); the browser sends their session cookie automatically.
|
||||
const href = civiBaseUrl
|
||||
? `${civiBaseUrl}/civicrm/file?reset=1&id=${encodeURIComponent(id)}`
|
||||
: "#";
|
||||
// Prefer the Civi-signed URL (carries the fcs JWT) returned by
|
||||
// Attachment.get; Civi's file handler crashes on a null fcs decode if we
|
||||
// hit /civicrm/file?id=X bare. Fall back to a bare URL only if signed
|
||||
// URLs weren't available (e.g. older Civi without `url` on Attachment).
|
||||
let href = "#";
|
||||
if (v.url) {
|
||||
href = v.url.startsWith("http")
|
||||
? v.url
|
||||
: `${civiBaseUrl}${v.url.startsWith("/") ? "" : "/"}${v.url}`;
|
||||
} else if (civiBaseUrl) {
|
||||
href = `${civiBaseUrl}/civicrm/file?reset=1&id=${encodeURIComponent(id)}`;
|
||||
}
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
|
||||
Reference in New Issue
Block a user