Branding and templates: per-domain, tenant and server logos, /logo, operator calendar email templates and RSVP page (BT-1 to BT-26)
Logos resolve domain, then tenant, then server-wide, then the built-in, with subdomains finding their domain. GET /logo serves a data-URL image, redirects to a URL logo without fetching it, sandboxes SVG, and answers 404 when no custom logo applies. Emails embed the first PNG, JPEG or GIF logo. Logo and template writes are checked; stored templates are read at send time, always escaped, and fall back to the built-in with a build warning when they don't parse. The RSVP page is served byte for byte with a CSP and no-referrer. The sign-in and RSVP pages load the logo through an image element. MT-22's session logo follows the chain to the server-wide logo. Acceptance tests 1 to 17; test 18 written as the ignored branding_compat.
This commit is contained in:
@@ -1055,28 +1055,18 @@
|
||||
function showDefault() { wrap.removeAttribute('data-loading'); }
|
||||
var target = apiUrl.replace(/\/api\/calendar\/rsvp$/, '') + '/logo';
|
||||
if (domain) target += '?domain=' + encodeURIComponent(domain);
|
||||
fetch(target, { method: 'GET', credentials: 'omit' })
|
||||
.then(function (res) {
|
||||
if (!res.ok) return null;
|
||||
var ct = (res.headers.get('content-type') || '').toLowerCase();
|
||||
if (ct.indexOf('image/') !== 0) return null;
|
||||
return res.blob();
|
||||
})
|
||||
.then(function (blob) {
|
||||
if (!blob) { showDefault(); return; }
|
||||
var objectUrl = URL.createObjectURL(blob);
|
||||
var img = new Image();
|
||||
img.className = 'custom-logo';
|
||||
img.alt = 'Logo';
|
||||
img.onload = function () {
|
||||
while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
|
||||
wrap.appendChild(img);
|
||||
wrap.removeAttribute('data-loading');
|
||||
};
|
||||
img.onerror = function () { URL.revokeObjectURL(objectUrl); showDefault(); };
|
||||
img.src = objectUrl;
|
||||
})
|
||||
.catch(function () { showDefault(); });
|
||||
// An image element, not fetch: it can follow /logo's redirect to
|
||||
// a URL logo on another origin (branding BT-26)
|
||||
var img = new Image();
|
||||
img.className = 'custom-logo';
|
||||
img.alt = 'Logo';
|
||||
img.onload = function () {
|
||||
while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
|
||||
wrap.appendChild(img);
|
||||
wrap.removeAttribute('data-loading');
|
||||
};
|
||||
img.onerror = showDefault;
|
||||
img.src = target;
|
||||
}
|
||||
|
||||
function findOrganizer(resp) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -390,34 +390,18 @@
|
||||
target = '/logo?domain=' + encodeURIComponent(loginHint.slice(at + 1).toLowerCase());
|
||||
}
|
||||
}
|
||||
fetch(target, { method: 'GET', credentials: 'same-origin' })
|
||||
.then(function (res) {
|
||||
if (!res.ok) return null;
|
||||
var ct = (res.headers.get('content-type') || '').toLowerCase();
|
||||
if (ct.indexOf('image/') !== 0) return null;
|
||||
return res.blob();
|
||||
})
|
||||
.then(function (blob) {
|
||||
if (!blob) { showDefault(); return; }
|
||||
var objectUrl = URL.createObjectURL(blob);
|
||||
var img = new Image();
|
||||
img.className = 'custom-logo';
|
||||
img.alt = 'Logo';
|
||||
img.onload = function () {
|
||||
while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
|
||||
wrap.appendChild(img);
|
||||
wrap.removeAttribute('data-loading');
|
||||
};
|
||||
img.onerror = function () {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
showDefault();
|
||||
};
|
||||
img.src = objectUrl;
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.log('Custom logo unavailable:', err);
|
||||
showDefault();
|
||||
});
|
||||
// An image element, not fetch: it can follow /logo's redirect to
|
||||
// a URL logo on another origin (branding BT-26)
|
||||
var img = new Image();
|
||||
img.className = 'custom-logo';
|
||||
img.alt = 'Logo';
|
||||
img.onload = function () {
|
||||
while (wrap.firstChild) wrap.removeChild(wrap.firstChild);
|
||||
wrap.appendChild(img);
|
||||
wrap.removeAttribute('data-loading');
|
||||
};
|
||||
img.onerror = showDefault;
|
||||
img.src = target;
|
||||
})();
|
||||
|
||||
function buildRequest(creds, otpValue) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Reference in New Issue
Block a user