source: Klonkt/src/assets/js/mod/admin-site-edit.js@ 013e16c

main
Last change on this file since 013e16c was 013e16c, checked in by Robin <roboburr@…>, 4 weeks ago

Naar SEO & social, want sameAs is schema.org (Robins vraag)

Hij vroeg of het niet onder SEO en social hoort, "omdat het ook een soort
koppeling is". Dat is het, en sterker dan dat: op die pagina staat een blok
"Uitgever (JSON-LD / rich results)", en sameAs IS een schema.org-eigenschap.
Zelfde vocabulaire, zelfde plek. Het paneel staat nu direct onder Uitgever.

DAARDOOR VOEDT EEN KOPPELING NU TWEE UITGANGEN. Naast schema:sameAs op de
fediverse-actor komt hij in de JSON-LD van de pagina zelf, in _publisher. Dat
is hetzelfde feit, verteld aan de andere lezer -- en het was gratis, want het
blok stond er al.

BETERE LABELS, want de oude waren geschreven voor het audioscherm. "Ben jij dit
in MusicBrainz?" werd "MusicBrainz (dezelfde artiest elders)" als legenda, met
een hint die zegt wat het OPLEVERT in plaats van alleen wat het is: je komt als
sameAs in de JSON-LD en in je fediverse-profiel, en er verandert niets bij
MusicBrainz zelf. Dat laatste is de geruststelling die er hoort te staan. Plus
een veldlabel ("Zoek jezelf op") met de toelichting "de naam waaronder je muziek
uitbrengt" -- want dat is de vraag, niet je sitenaam. Drie talen.

Het zoekveld begint nu op publisher_name en valt terug op de sitetitel: op deze
pagina heb je die naam net ingevuld.

Meeverhuisd: het endpoint (/admin/seo/api/musicbrainz) en het clientblok, in een
nieuwe mod/admin-seo.js. Die pagina had nog geen JavaScript; opzoeken vraagt er
wel om. Het profielscherm is weer helemaal schoon.

Co-Authored-By: Claude Opus 5 <noreply@…>

  • Property mode set to 100644
File size: 5.1 KB
Line 
1// Site bewerken in beheer (pages/admin-site-edit.ejs) -- verplaatst uit inline script, shaer-bqr.
2//
3// Inline script in een pagina wordt door de CSP geweigerd zodra je die pagina via
4// een link BINNEN de site opent: de nonce rouleert per verzoek (shaer-0i6). Dit
5// bestand wordt door de bootstrap in shell.ejs geladen en heeft dat probleem niet.
6//
7// Alles hier hoort GEDELEGEERD te luisteren (op document, niet op een element dat
8// er nu staat) en tegen een tweede aanroep te kunnen.
9
10// Element-bedrading leeft zo lang als de pagina; de bootstrap roept init()
11// aan bij elke paginawissel waarop deze module actief is (shaer-5s1).
12export function init() { run(); }
13
14function run() {
15 (function(){
16 if (window.__themePreviewWired) return; window.__themePreviewWired = true;
17 var html = document.documentElement;
18 function applyAccent(hex){
19 if(!/^#[0-9a-fA-F]{6}$/.test(hex)) return;
20 var sa = document.getElementById('pcms-site-accent');
21 if(!sa){ sa = document.createElement('style'); sa.id = 'pcms-site-accent'; document.head.appendChild(sa); }
22 sa.textContent = ':root,[data-palette]{--accent:'+hex+';--accent-soft:color-mix(in srgb,'+hex+' 80%,white);--accent-tint:color-mix(in srgb,'+hex+' 12%,transparent);}';
23 }
24 document.addEventListener('change', function(e){
25 var t = e.target; if(!t || !t.name) return;
26 if(t.name === 'palette'){ html.setAttribute('data-palette', t.value); }
27 else if(t.name === 'accent'){ applyAccent(t.value); }
28 else if(t.name === 'theme_override'){
29 if(t.value === 'light' || t.value === 'dark'){ html.setAttribute('data-theme', t.value); }
30 else { var dk = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; html.setAttribute('data-theme', dk ? 'dark' : 'light'); }
31 }
32 });
33 })();
34
35
36// ── volgend blok ──
37
38(function() {
39 // Slug URL field: show the real host as a dimmed prefix (the slug itself is
40 // coloured via CSS). Hub → host/user/<slug>, otherwise host/<slug>.
41 var sh = document.getElementById('slug-host');
42 if (sh) sh.textContent = location.host + (sh.dataset.prefix || '/');
43})();
44(function() {
45 // Profile-links repeater: add row from <template>, remove on click.
46 var rows = document.getElementById('profile-links-rows');
47 var tpl = document.getElementById('profile-link-template');
48 var add = document.getElementById('profile-link-add');
49 if (!rows || !tpl || !add) return;
50
51 add.addEventListener('click', function() {
52 var clone = tpl.content.cloneNode(true);
53 rows.appendChild(clone);
54 });
55 rows.addEventListener('click', function(e) {
56 if (e.target && e.target.classList.contains('pl-remove')) {
57 var row = e.target.closest('.profile-link-row');
58 if (row) row.remove();
59 }
60 });
61})();
62
63// P63 — Profile photo picker: upload via /admin/sites/upload-photo,
64// then write the returned URL into the visible input. Live thumb preview.
65(function() {
66 var picker = document.getElementById('photo-picker');
67 if (!picker) return;
68 var thumb = document.getElementById('photo-thumb');
69 var preview = document.getElementById('photo-preview');
70 var urlEl = document.getElementById('photo-url');
71 var trigger = document.getElementById('photo-upload-trigger');
72 var clear = document.getElementById('photo-clear');
73 var field = document.getElementById('photo-upload-field');
74 var status = document.getElementById('photo-status');
75
76 function showPreview(url) {
77 if (url) {
78 preview.src = url;
79 preview.hidden = false;
80 thumb.removeAttribute('data-empty');
81 clear.hidden = false;
82 } else {
83 preview.src = '';
84 preview.hidden = true;
85 thumb.setAttribute('data-empty', '');
86 clear.hidden = true;
87 }
88 }
89
90 if (urlEl) {
91 urlEl.addEventListener('input', function() {
92 showPreview(urlEl.value.trim());
93 });
94 }
95
96 if (trigger && field) {
97 trigger.addEventListener('click', function() { field.click(); });
98 field.addEventListener('change', async function() {
99 var file = field.files && field.files[0];
100 if (!file) return;
101 status.classList.remove('is-error');
102 status.textContent = 'Uploaden…';
103 try {
104 var fd = new FormData();
105 fd.append('photo', file);
106 var r = await fetch('/admin/sites/upload-photo', {
107 method: 'POST',
108 body: fd,
109 credentials: 'same-origin',
110 });
111 var j = await r.json();
112 if (!r.ok || !j.ok) throw new Error(j.error || ('Upload mislukt (' + r.status + ')'));
113 urlEl.value = j.url;
114 showPreview(j.url);
115 status.textContent = 'Geüpload ✓';
116 setTimeout(function() { status.textContent = ''; }, 2000);
117 } catch (e) {
118 status.classList.add('is-error');
119 status.textContent = 'Mislukt: ' + e.message;
120 } finally {
121 field.value = '';
122 }
123 });
124 }
125
126 if (clear) {
127 clear.addEventListener('click', function() {
128 urlEl.value = '';
129 showPreview('');
130 status.textContent = '';
131 // Note: doesn't delete the file from disk — saving the form with empty
132 // URL leaves the file orphaned on the server. Acceptable for now.
133 });
134 }
135})();
136
137}
Note: See TracBrowser for help on using the repository browser.