Testzugang: eigene Anfrageseite DE+EN und Einstiege auf der Startseite
Neue Seite /testzugang/ und Spiegel /en/trial-access/ mit Anfrageformular fuer einen einwoechigen Testzugang. Pflicht ist eine dienstliche E-Mail-Adresse, Freemail- und Wegwerf-Anbieter werden abgewiesen, Behoerden- und Organisationsdomains werden erkannt und in der Anfrage-Mail gekennzeichnet. Spamschutz ohne Drittanbieter, also Honeypot-Feld und Zeitfalle statt Captcha, damit kein externer Dienst eingebunden werden muss. Der Versand laeuft ueber den bestehenden Endpunkt /api/contact, die Zusatzfelder stehen strukturiert im Nachrichtentext. Einstiege auf der Startseite DE+EN, also Knopf in der Navigation, zwei Knoepfe im Kontakt-Abschnitt, Verlinkung der FAQ-Antwort zum Zugang und ein Footer-Link. Der Hero-Knopf der sechs Loesungsseiten zeigt jetzt ebenfalls auf die Testzugang-Seite. Sitemap und llms.txt ergaenzt.
Dieser Commit ist enthalten in:
244
js/trial-form.js
Normale Datei
244
js/trial-form.js
Normale Datei
@@ -0,0 +1,244 @@
|
||||
/* ==================================================================
|
||||
AegisSight Testzugang-Formular
|
||||
Wird von /testzugang/ (DE) und /en/trial-access/ (EN) genutzt.
|
||||
Sprache wird aus <html lang> gelesen, wie in lagebild.js und
|
||||
cookie-consent.js auch.
|
||||
================================================================== */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var LANG = (document.documentElement.lang || 'de').toLowerCase().indexOf('en') === 0 ? 'en' : 'de';
|
||||
|
||||
/* ---------- Texte ---------- */
|
||||
var T = {
|
||||
de: {
|
||||
sending: 'Wird gesendet...',
|
||||
send: 'Testzugang anfragen',
|
||||
freemail: 'Bitte geben Sie Ihre dienstliche E-Mail-Adresse an. Adressen bei Freemail-Anbietern wie Gmail, GMX oder web.de können wir für einen Testzugang nicht berücksichtigen.',
|
||||
invalidMail: 'Diese E-Mail-Adresse sieht nicht vollständig aus. Bitte prüfen Sie Ihre Eingabe.',
|
||||
consent: 'Bitte bestätigen Sie die Datenschutzhinweise, damit wir Ihre Anfrage bearbeiten dürfen.',
|
||||
tooFast: 'Bitte nehmen Sie sich einen Moment für das Formular. Versuchen Sie es gleich noch einmal.',
|
||||
sendError: 'Die Anfrage konnte nicht gesendet werden.',
|
||||
netError: 'Verbindungsfehler. Bitte versuchen Sie es erneut.',
|
||||
hintOfficial: 'Dienstliche Adresse erkannt.',
|
||||
labels: {
|
||||
head: 'ANFRAGE TESTZUGANG AEGISSIGHT MONITOR',
|
||||
name: 'Name',
|
||||
org: 'Behörde oder Organisation',
|
||||
role: 'Funktion oder Dienststelle',
|
||||
mail: 'Dienstliche E-Mail',
|
||||
phone: 'Telefon',
|
||||
users: 'Geplante Nutzerzahl',
|
||||
topic: 'Gewünschtes Thema und Anwendungsfall',
|
||||
start: 'Gewünschter Start',
|
||||
domain: 'Domain-Einstufung',
|
||||
domainOk: 'Behörden- oder Organisationsdomain erkannt',
|
||||
domainCheck: 'nicht eindeutig, bitte manuell prüfen',
|
||||
empty: 'keine Angabe'
|
||||
}
|
||||
},
|
||||
en: {
|
||||
sending: 'Sending...',
|
||||
send: 'Request trial access',
|
||||
freemail: 'Please enter your official work email address. Addresses from free providers such as Gmail, GMX or web.de cannot be accepted for trial access.',
|
||||
invalidMail: 'This email address looks incomplete. Please check your entry.',
|
||||
consent: 'Please confirm the privacy notice so that we may process your request.',
|
||||
tooFast: 'Please take a moment to fill in the form, then try again.',
|
||||
sendError: 'The request could not be sent.',
|
||||
netError: 'Connection error. Please try again.',
|
||||
hintOfficial: 'Official address recognised.',
|
||||
labels: {
|
||||
head: 'TRIAL ACCESS REQUEST AEGISSIGHT MONITOR',
|
||||
name: 'Name',
|
||||
org: 'Authority or organisation',
|
||||
role: 'Role or department',
|
||||
mail: 'Work email',
|
||||
phone: 'Phone',
|
||||
users: 'Planned number of users',
|
||||
topic: 'Requested topic and use case',
|
||||
start: 'Preferred start',
|
||||
domain: 'Domain assessment',
|
||||
domainOk: 'official domain recognised',
|
||||
domainCheck: 'not conclusive, please review manually',
|
||||
empty: 'not provided'
|
||||
}
|
||||
}
|
||||
}[LANG];
|
||||
|
||||
/* ---------- Freemail- und Wegwerf-Anbieter ----------
|
||||
Diese Adressen werden abgewiesen. Alles andere kommt durch,
|
||||
damit Landkreise, Hochschulen und Rettungsdienste mit eigener
|
||||
Domain nicht ausgesperrt werden. */
|
||||
var BLOCKED = [
|
||||
'gmail.com', 'googlemail.com', 'gmx.de', 'gmx.net', 'gmx.at', 'gmx.ch', 'gmx.com',
|
||||
'web.de', 't-online.de', 'freenet.de', 'arcor.de', 'online.de',
|
||||
'outlook.com', 'outlook.de', 'hotmail.com', 'hotmail.de', 'live.com', 'live.de', 'msn.com',
|
||||
'yahoo.com', 'yahoo.de', 'yahoo.co.uk', 'ymail.com', 'aol.com',
|
||||
'icloud.com', 'me.com', 'mac.com',
|
||||
'proton.me', 'protonmail.com', 'pm.me', 'posteo.de', 'posteo.net', 'mailbox.org',
|
||||
'mail.de', 'mail.com', 'email.de', 'firemail.de', 'unitybox.de', 'vodafone.de', 'vodafonemail.de',
|
||||
'zoho.com', 'fastmail.com', 'hey.com', 'tutanota.com', 'tutanota.de', 'tuta.io',
|
||||
'yandex.com', 'yandex.ru', 'mail.ru', 'qq.com', '163.com', '126.com', 'sina.com',
|
||||
'mailinator.com', 'guerrillamail.com', '10minutemail.com', 'temp-mail.org', 'tempmail.com',
|
||||
'trashmail.com', 'trashmail.de', 'wegwerfmail.de', 'yopmail.com', 'sharklasers.com',
|
||||
'getnada.com', 'dispostable.com', 'maildrop.cc', 'throwawaymail.com', 'moakt.com',
|
||||
'emailondeck.com', 'spam4.me', 'byom.de', 'einrot.com', 'mytemp.email'
|
||||
];
|
||||
|
||||
/* ---------- Muster typischer Behörden- und Organisationsdomains ----------
|
||||
Nur zur Kennzeichnung in der Anfrage-Mail, keine Sperre. */
|
||||
var OFFICIAL_SUFFIX = [
|
||||
'.bund.de', '.bundeswehr.org', '.bundestag.de', '.bundesrat.de',
|
||||
'.bayern.de', '.nrw.de', '.berlin.de', '.hamburg.de', '.bremen.de',
|
||||
'.niedersachsen.de', '.hessen.de', '.rlp.de', '.saarland.de', '.sachsen.de',
|
||||
'.sachsen-anhalt.de', '.thueringen.de', '.brandenburg.de', '.schleswig-holstein.de',
|
||||
'.mv-regierung.de', '.bwl.de', '.baden-wuerttemberg.de',
|
||||
'.europa.eu', '.europarl.europa.eu', '.gv.at', '.bmi.gv.at', '.admin.ch',
|
||||
'.gov', '.gov.uk', '.mil', '.gouv.fr', '.overheid.nl', '.edu'
|
||||
];
|
||||
var OFFICIAL_PART = [
|
||||
'polizei.', 'bundeswehr.', 'bundespolizei.', 'zoll.', 'thw.', 'feuerwehr.',
|
||||
'stadt-', 'stadt.', 'kreis-', 'landkreis-', 'kreis.', 'bezirk-', 'gemeinde-',
|
||||
'uni-', 'universitaet', 'university', 'hochschule', 'fh-', 'hs-',
|
||||
'landtag', 'ministerium', 'verfassungsschutz', 'bka.', 'bfv.', 'lka.'
|
||||
];
|
||||
|
||||
function domainOf(mail) {
|
||||
var at = (mail || '').lastIndexOf('@');
|
||||
return at === -1 ? '' : mail.slice(at + 1).trim().toLowerCase();
|
||||
}
|
||||
|
||||
function isBlocked(mail) {
|
||||
return BLOCKED.indexOf(domainOf(mail)) !== -1;
|
||||
}
|
||||
|
||||
function looksOfficial(mail) {
|
||||
var d = domainOf(mail);
|
||||
if (!d) return false;
|
||||
var i;
|
||||
for (i = 0; i < OFFICIAL_SUFFIX.length; i++) {
|
||||
if (d === OFFICIAL_SUFFIX[i].slice(1) || d.slice(-OFFICIAL_SUFFIX[i].length) === OFFICIAL_SUFFIX[i]) return true;
|
||||
}
|
||||
for (i = 0; i < OFFICIAL_PART.length; i++) {
|
||||
if (d.indexOf(OFFICIAL_PART[i]) !== -1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function plausibleMail(mail) {
|
||||
return /^[^\s@]+@[^\s@.]+(\.[^\s@.]+)+$/.test(mail || '');
|
||||
}
|
||||
|
||||
/* ---------- Initialisierung ---------- */
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var form = document.getElementById('trial-form');
|
||||
if (!form) return;
|
||||
|
||||
var loadedAt = Date.now();
|
||||
var mailField = document.getElementById('tf-email');
|
||||
var mailNote = document.getElementById('tf-email-note');
|
||||
|
||||
/* Live-Rueckmeldung am E-Mail-Feld */
|
||||
if (mailField && mailNote) {
|
||||
mailField.addEventListener('blur', function () {
|
||||
var v = mailField.value.trim();
|
||||
mailNote.className = 'tf-note';
|
||||
if (!v) { mailNote.textContent = ''; return; }
|
||||
if (isBlocked(v)) {
|
||||
mailNote.textContent = T.freemail;
|
||||
mailNote.className = 'tf-note tf-note-error';
|
||||
mailField.setAttribute('aria-invalid', 'true');
|
||||
} else if (looksOfficial(v)) {
|
||||
mailNote.textContent = T.hintOfficial;
|
||||
mailNote.className = 'tf-note tf-note-ok';
|
||||
mailField.removeAttribute('aria-invalid');
|
||||
} else {
|
||||
mailNote.textContent = '';
|
||||
mailField.removeAttribute('aria-invalid');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var val = function (id) {
|
||||
var el = document.getElementById(id);
|
||||
return el ? el.value.trim() : '';
|
||||
};
|
||||
|
||||
var email = val('tf-email');
|
||||
var honey = val('tf-website');
|
||||
var consent = document.getElementById('tf-consent');
|
||||
var errorBox = document.getElementById('tf-error');
|
||||
var btn = form.querySelector('button[type="submit"]');
|
||||
|
||||
var fail = function (msg) {
|
||||
if (errorBox) {
|
||||
errorBox.textContent = msg;
|
||||
errorBox.style.display = 'block';
|
||||
errorBox.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
if (btn) { btn.disabled = false; btn.textContent = T.send; }
|
||||
};
|
||||
|
||||
if (errorBox) errorBox.style.display = 'none';
|
||||
|
||||
/* Honeypot. Echte Nutzer sehen dieses Feld nicht. */
|
||||
if (honey) return false;
|
||||
|
||||
/* Zeitfalle gegen Bots */
|
||||
if (Date.now() - loadedAt < 4000) return fail(T.tooFast);
|
||||
|
||||
if (!plausibleMail(email)) return fail(T.invalidMail);
|
||||
if (isBlocked(email)) return fail(T.freemail);
|
||||
if (consent && !consent.checked) return fail(T.consent);
|
||||
|
||||
if (btn) { btn.disabled = true; btn.textContent = T.sending; }
|
||||
|
||||
var L = T.labels;
|
||||
var e_ = L.empty;
|
||||
var body = [
|
||||
L.head,
|
||||
'',
|
||||
L.org + ' = ' + (val('tf-org') || e_),
|
||||
L.role + ' = ' + (val('tf-role') || e_),
|
||||
L.mail + ' = ' + email,
|
||||
L.phone + ' = ' + (val('tf-phone') || e_),
|
||||
L.users + ' = ' + (val('tf-users') || e_),
|
||||
L.start + ' = ' + (val('tf-start') || e_),
|
||||
'',
|
||||
L.topic,
|
||||
val('tf-topic') || e_,
|
||||
'',
|
||||
L.domain + ' = ' + (looksOfficial(email) ? L.domainOk : L.domainCheck)
|
||||
].join('\n');
|
||||
|
||||
fetch('/api/contact', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: val('tf-name'),
|
||||
organisation: val('tf-org'),
|
||||
email: email,
|
||||
message: body,
|
||||
lang: LANG
|
||||
})
|
||||
})
|
||||
.then(function (r) { return r.json().then(function (d) { return { ok: r.ok, data: d }; }); })
|
||||
.then(function (res) {
|
||||
if (res.ok) {
|
||||
form.style.display = 'none';
|
||||
var ok = document.getElementById('tf-success');
|
||||
if (ok) { ok.style.display = 'block'; ok.scrollIntoView({ behavior: 'smooth', block: 'center' }); }
|
||||
if (window.umami) umami.track('trial_request_success');
|
||||
} else {
|
||||
fail((res.data && res.data.error) || T.sendError);
|
||||
}
|
||||
})
|
||||
.catch(function () { fail(T.netError); });
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
})();
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren