'use strict'; /** * tools/manual/capture_extra.js: nimmt die neuen Bilder für das Handbuch auf (Passkeys, Anmeldeseite, Mitarbeiter-Menü, Anmelde-Wächter) (Demo-Installation mit erfundenen Daten, siehe seed_manual.php). * 1. php tools/demo-video/seed_demo.php && php tools/manual/seed_manual.php && php tools/manual/install_plugins.php * 2. DESKHUB_CATALOG_DEFAULTS=tests/fixtures/no-catalog.php php -S 127.0.0.1:8097 -t * 3. electron tools/manual/capture_manual.js */ const { app, BrowserWindow } = require('electron'); const fs = require('fs'); const path = require('path'); const BASE = 'http://127.0.0.1:8097'; const OUT = path.resolve(process.argv[2] || path.join(__dirname, 'shots')); const W = 1180; // schmaler als ein Bildschirm: Die Bilder werden auf A4 verkleinert und müssen lesbar bleiben const SIDEBAR = 240; // Breite der Seitenleiste; sie wird aus den Bildern der Seiten herausgeschnitten (eigenes Bild vom Menü) const H = 860; const MAXH = 2400; fs.mkdirSync(OUT, { recursive: true }); const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); let win; const run = (js) => win.webContents.executeJavaScript(js, true); const report = { ok: [], fail: [] }; /** Seite laden und alles entfernen, was nur zur Demo-Installation gehört (SQLite-Hinweis, Zähler der Sitzung). */ async function load(url, wait = 700) { await win.loadURL(BASE + url); await sleep(wait); await run(`(() => { document.getElementById('session-timer-bar')?.remove(); document.querySelectorAll('div,p,section,aside').forEach((e) => { if (e.children.length < 8 && /SQLite-Modus aktiv/.test(e.textContent) && e.textContent.length < 400) e.remove(); }); })()`); } /** Bild der Seite speichern: höchstens MAXH hoch; mit full = false nur der sichtbare Teil. */ async function shot(name, { full = true, clip = null, keepSidebar = false } = {}) { let h = H; if (full) { h = await run('Math.max(document.documentElement.scrollHeight, document.body.scrollHeight)'); h = Math.min(Math.max(h, 500), MAXH); } win.setContentSize(W, h); await sleep(350); const hasSidebar = await run(`(() => { const s = document.getElementById('sidebar'); return !!s && s.getBoundingClientRect().width > 100; })()`); let img = await win.webContents.capturePage(); if (clip) img = img.crop(clip); else if (hasSidebar && !keepSidebar) img = img.crop({ x: SIDEBAR, y: 0, width: W - SIDEBAR, height: h }); fs.writeFileSync(path.join(OUT, name + '.png'), img.toPNG()); win.setContentSize(W, H); report.ok.push(name); } async function page(name, url, opts = {}) { try { await load(url, opts.wait); if (opts.prep) { await run(opts.prep); await sleep(opts.prepWait || 400); } await shot(name, opts); } catch (e) { report.fail.push(name + ': ' + e.message); } } async function login(email, pass, loginUrl) { await load(loginUrl, 600); await run(`(() => { const f = document.querySelector('form'); f.querySelector('[name=email]').value = ${JSON.stringify(email)}; f.querySelector('[name=password]').value = ${JSON.stringify(pass)}; f.submit(); })()`); await sleep(1500); } app.whenReady().then(async () => { win = new BrowserWindow({ width: W, height: H, show: true, useContentSize: true, webPreferences: { contextIsolation: true, backgroundThrottling: false } }); try { // Anmeldeseite mit dem Passkey-Knopf (im Electron-Fenster ohne Passkey-Unterstützung wird er von Hand eingeblendet) await page('staff-login', '/staff/login.php', { full: false, prep: `(() => { const b = document.getElementById('passkey-login'); if (b) b.style.display = 'block'; })()` }); await login('lena@demo.example', 'Demo-Passwort-1234', '/staff/login.php'); await page('profil-sicherheit', '/staff/profile.php?tab=security', { prep: `(() => { const a = document.getElementById('passkey-add'); if (a && !a.getAttribute('data-off')) a.style.display = 'block'; const u = document.getElementById('pk_unsupported'); if (u) u.style.display = 'none'; })()` }); await page('admin-anmelde-waechter', '/staff/login_log.php'); await page('admin-mitarbeiter-passkeys', '/staff/staff_manage.php?edit=3', { prep: `(() => { const h = [...document.querySelectorAll('h3')].find((x) => x.textContent.includes('Passkeys von')); if (!h) return; const card = h.closest('.card'); let n = card.previousElementSibling; while (n) { const p = n.previousElementSibling; if (n.tagName !== 'STYLE') n.style.display = 'none'; n = p; } })()` }); await page('admin-menue-aktionen', '/staff/staff_manage.php', { full: false, prep: `(() => { const row = [...document.querySelectorAll('#sm-table tbody tr')].find((r) => r.textContent.includes('Tim')); row?.querySelector('.sm-menu summary')?.click(); })()` }); } catch (e) { report.fail.push('FATAL ' + e.stack); } fs.writeFileSync(path.join(OUT, '_bericht_extra.json'), JSON.stringify(report, null, 1)); console.log(`Aufgenommen: ${report.ok.length}, Fehler: ${report.fail.length}`); report.fail.forEach((f) => console.log(' - ' + f)); app.quit(); });