fix(freelancer-directory): render an uploaded photo (data:image/), not only a linked URL

Both card renderers emitted an <img> only for a URL, so a record whose Image is an
inline data:image/... URI (what the upload step stores today) fell through to the
monogram. The avatar check now allowlists exactly two schemes — https:// and
data:image/ — in build-directory.py and in the page script; anything else (empty,
junk like "gineoirn", another scheme) still falls back to the monogram, and the
value never reaches src unchecked. The lede copy is corrected to match: a photo
shows where the freelancer gave a link OR uploaded an image.
This commit is contained in:
webmaster committed 2026-11-03 13:04:06 +00:00
1 parent e067fec168
commit 5ce14ea666
1 file changed
+12 -3
+12 -3
View File
@@ -782,7 +782,7 @@
<section class="section-dark" id="directory"> <section class="section-dark" id="directory">
<div class="container"> <div class="container">
<h2 class="section-title">Freelancers available</h2> <h2 class="section-title">Freelancers available</h2>
<p class="dir-lede">Listings are for freelancers who work on their own — that is the declaration we ask each of them for, and we do not verify it. Where a card shows a number of projects, that is a number we recorded — not a rating, a score or an endorsement. A card shows a photo only where the freelancer gave us a link to one; otherwise it shows their initials. We make no claim about a freelancer beyond what they wrote and what we can count ourselves.</p> <p class="dir-lede">Listings are for freelancers who work on their own — that is the declaration we ask each of them for, and we do not verify it. Where a card shows a number of projects, that is a number we recorded — not a rating, a score or an endorsement. A card shows a photo whether the freelancer gave us a link to one or uploaded an image; otherwise it shows their initials. We make no claim about a freelancer beyond what they wrote and what we can count ourselves.</p>
<!-- Rendered at build time from our freelancer webhook (one call, no CRM), <!-- Rendered at build time from our freelancer webhook (one call, no CRM),
so the list works with JavaScript disabled. The filter controls below are so the list works with JavaScript disabled. The filter controls below are
@@ -1741,6 +1741,15 @@
return v.indexOf("http://") === 0 || v.indexOf("https://") === 0 || v.indexOf("/") === 0; return v.indexOf("http://") === 0 || v.indexOf("https://") === 0 || v.indexOf("/") === 0;
} }
/* A card renders a photo only for an allowlisted scheme: an https URL or an inline
data:image/ URI. Anything else — empty, junk like "gineoirn", another scheme —
falls back to the monogram. The freelancer controls this field, so it never
reaches src unchecked. (Separate from isLink(), which the *link* renderer also
uses and which accepts http:// and relative paths.) */
function isImageSrc(v) {
return v.indexOf("https://") === 0 || v.indexOf("data:image/") === 0;
}
function liveEl(tag, cls, text) { function liveEl(tag, cls, text) {
var node = document.createElement(tag); var node = document.createElement(tag);
if (cls) node.className = cls; if (cls) node.className = cls;
@@ -1786,8 +1795,8 @@
var photo = String(rec.Image || "").trim(); var photo = String(rec.Image || "").trim();
var avatar; var avatar;
if (isLink(photo)) { if (isImageSrc(photo)) {
avatar = liveEl("img", "dev-photo"); // a real URL only — never a base64 blob avatar = liveEl("img", "dev-photo"); // https URL or data:image/ URI only
avatar.src = photo; avatar.src = photo;
avatar.alt = ""; avatar.alt = "";
avatar.width = 72; avatar.width = 72;