Add Turnstile protection and harden export workflow
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/app.php';
|
||||
app_send_security_headers();
|
||||
include "zefixAPI.php";
|
||||
$turnstileSiteKey = app_turnstile_is_configured() ? app_turnstile_site_key() : '';
|
||||
$formStartedToken = app_create_form_started_token();
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
@@ -8,9 +12,12 @@ include "zefixAPI.php";
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- Bootstrap first, then your overrides -->
|
||||
<link rel="stylesheet" href="css/bootstrap.css">
|
||||
<link rel="stylesheet" href="css/custom.css?v=1.3">
|
||||
<link rel="stylesheet" href="css/custom.css?v=2.2">
|
||||
|
||||
<?php if ($turnstileSiteKey !== ''): ?>
|
||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||
<?php endif; ?>
|
||||
|
||||
<title>Silias Zefix Export</title>
|
||||
|
||||
@@ -41,14 +48,28 @@ include "zefixAPI.php";
|
||||
}
|
||||
|
||||
.gemeindeeintrag { margin-bottom: .25rem; }
|
||||
|
||||
.bot-trap {
|
||||
position: absolute !important;
|
||||
left: -10000px !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-light">
|
||||
|
||||
<nav class="navbar navbar-light bg-white border-bottom">
|
||||
<nav class="navbar navbar-light site-header border-bottom py-2">
|
||||
<div class="container">
|
||||
<span class="navbar-brand mb-0 h1">Silias • ZEFIX Export</span>
|
||||
<a class="brand-lockup" href="index.php" aria-label="Silias Zefix Export – Startseite">
|
||||
<span class="brand-logo-crop" aria-hidden="true">
|
||||
<img src="img/silias-logo.png" alt="">
|
||||
</span>
|
||||
<span class="brand-divider" aria-hidden="true"></span>
|
||||
<span class="brand-product">Zefix Export<small>Ein kostenloses Silias-Tool</small></span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -56,53 +77,73 @@ include "zefixAPI.php";
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10 col-xl-9">
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="mb-4 mb-md-5">
|
||||
<div class="hero-eyebrow mb-2">Zentraler Firmenindex</div>
|
||||
<h1 class="display-6 fw-bold hero-title mb-3">Firmendaten gezielt auswählen und als CSV exportieren</h1>
|
||||
<p class="lead hero-copy mb-0">
|
||||
Filtern Sie Unternehmen nach Firmenname, Ort und Rechtsform. Sobald der Export bereit ist,
|
||||
erhalten Sie einen sicheren Download-Link per E-Mail.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card export-shell shadow-sm">
|
||||
<div class="card-body p-4 p-md-5">
|
||||
|
||||
<div class="d-flex flex-column flex-md-row align-items-md-start justify-content-between gap-2 mb-3">
|
||||
<div>
|
||||
<h1 class="h3 mb-1">Silias Zefix Export</h1>
|
||||
<p class="text-muted mb-0">
|
||||
Exportiere Firmen als Excel (csv) aus dem Zentralen Firmenindex (ZEFIX).
|
||||
</p>
|
||||
</div>
|
||||
<span class="badge bg-primary-subtle text-primary border border-primary-subtle align-self-md-center">
|
||||
Export via E-Mail Link
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<form action="submit.php" method="post" class="needs-validation" novalidate>
|
||||
|
||||
<!-- Firmenname -->
|
||||
<div class="mb-4">
|
||||
<label for="firma" class="form-label">Firmenname (optional)</label>
|
||||
<div class="form-text mb-2">* kann als Platzhalter verwendet werden</div>
|
||||
<input type="text" id="firma" name="firma" class="form-control" placeholder="z.B. *solar*">
|
||||
<input type="hidden" name="form_started" value="<?php echo htmlspecialchars($formStartedToken, ENT_QUOTES); ?>">
|
||||
<div class="bot-trap" aria-hidden="true" hidden>
|
||||
<label for="website">Website</label>
|
||||
<input type="text" id="website" name="website" value="" tabindex="-1" autocomplete="off">
|
||||
</div>
|
||||
|
||||
<!-- Kanton -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Kanton</label>
|
||||
<div class="alert alert-warning py-2 mb-2" role="alert" style="font-size: .95rem;">
|
||||
Die Kantonsauswahl wird vom Export ignoriert und dient nur zum Filtern der Ortschaften.
|
||||
Für Export nach Kanton: gewünschte Kantone wählen und dann bei Ortschaften auf
|
||||
<b>Alle Ergebnisse auswählen</b> klicken.
|
||||
<div class="form-section mb-3">
|
||||
<div class="section-heading">
|
||||
<span class="section-number" aria-hidden="true">1</span>
|
||||
<div>
|
||||
<h2>Firmenname</h2>
|
||||
<p>Optional: Schränken Sie den Export auf passende Firmennamen ein.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="kantonauswahl" class="scrollWindow" onclick="filterFunction()"></div>
|
||||
<label for="firma" class="form-label">Firmenname oder Suchmuster</label>
|
||||
<input type="text" id="firma" name="firma" class="form-control" placeholder="Zum Beispiel *solar*" maxlength="200">
|
||||
<div class="form-text mt-2">Das Sternchen (*) kann als Platzhalter verwendet werden.</div>
|
||||
</div>
|
||||
|
||||
<!-- Sitz / Ort -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Sitz (Ort)</label>
|
||||
<div class="form-section mb-3">
|
||||
<div class="section-heading">
|
||||
<span class="section-number" aria-hidden="true">2</span>
|
||||
<div>
|
||||
<h2>Ort auswählen</h2>
|
||||
<p>Filtern Sie zuerst nach Kanton oder Ortsname und übernehmen Sie danach die gewünschten Orte.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-primary border-0 py-2 mb-3" role="note">
|
||||
<strong>Hinweis:</strong> Die Kantone filtern nur die Ortsliste. Klicken Sie danach auf
|
||||
<strong>«Gefilterte Orte auswählen»</strong>, damit die Orte in den Export übernommen werden.
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label class="form-label mb-0">Kanton</label>
|
||||
<span class="selection-meta" id="cantonCount">0 ausgewählt</span>
|
||||
</div>
|
||||
<div id="kantonauswahl" class="scrollWindow" onclick="filterFunction()"></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<label for="sitzInput" class="form-label mb-0">Sitz (Ort)</label>
|
||||
<span class="selection-meta" id="seatCount">0 ausgewählt</span>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 bg-white">
|
||||
<div class="card-body p-0">
|
||||
<div class="d-flex flex-column flex-lg-row gap-2 mb-2">
|
||||
<div class="flex-grow-1">
|
||||
<input type="text"
|
||||
placeholder="Suchen (Ort)"
|
||||
placeholder="Ort suchen"
|
||||
id="sitzInput"
|
||||
onkeyup="filterFunction()"
|
||||
onchange="filterFunction()"
|
||||
@@ -112,7 +153,7 @@ include "zefixAPI.php";
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button type="button" class="btn btn-outline-primary button-small" onclick="gefundeneGemeindenAuswählen()">
|
||||
Alle Ergebnisse auswählen
|
||||
Gefilterte Orte auswählen
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary button-small" onclick="ausgewählteGemeindenLöschen()">
|
||||
Auswahl löschen
|
||||
@@ -121,7 +162,7 @@ include "zefixAPI.php";
|
||||
Alle anzeigen
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary button-small" onclick="nurAusgewählteGemeindenAnzeigen()">
|
||||
Nur ausgewählte
|
||||
Nur ausgewählte anzeigen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -130,25 +171,43 @@ include "zefixAPI.php";
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rechtsform -->
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Rechtsform</label>
|
||||
<div class="form-section mb-3">
|
||||
<div class="section-heading">
|
||||
<span class="section-number" aria-hidden="true">3</span>
|
||||
<div>
|
||||
<h2>Rechtsform auswählen</h2>
|
||||
<p>Wählen Sie mindestens eine Rechtsform für den Export.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-2">
|
||||
<div>
|
||||
<button type="button" class="btn btn-outline-primary button-small" onclick="alleRechtsformenAuswählen()">
|
||||
Alle auswählen
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary button-small" onclick="rechtsformenLöschen()">
|
||||
Auswahl löschen
|
||||
</button>
|
||||
</div>
|
||||
<span class="selection-meta" id="legalFormCount">0 ausgewählt</span>
|
||||
</div>
|
||||
|
||||
<div id="rechtsformenauswahl" class="scrollWindow"></div>
|
||||
</div>
|
||||
|
||||
<!-- Optionen -->
|
||||
<div class="mb-4">
|
||||
<div class="form-section mb-3">
|
||||
<div class="section-heading">
|
||||
<span class="section-number" aria-hidden="true">4</span>
|
||||
<div>
|
||||
<h2>Export anfordern</h2>
|
||||
<p>Geben Sie die Zieladresse an und starten Sie den geschützten Export.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="form-label mb-2">Weitere Optionen</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="exakteSuche" name="exakteSuche" disabled>
|
||||
<label class="form-check-label" for="exakteSuche">Exakte Suche (noch in Entwicklung)</label>
|
||||
@@ -168,23 +227,43 @@ include "zefixAPI.php";
|
||||
<input class="form-check-input" type="checkbox" id="phonetischeSuche" name="phonetischeSuche" disabled>
|
||||
<label class="form-check-label" for="phonetischeSuche">Phonetische Suche (noch in Entwicklung)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div class="mb-4">
|
||||
<label for="email" class="form-label">Ihre E-Mail Adresse</label>
|
||||
<label for="email" class="form-label">Ihre E-Mail-Adresse</label>
|
||||
<div class="form-text mb-2">
|
||||
Das Aufbereiten der Daten kann länger dauern. Sie erhalten einen Download-Link per E-Mail sobald die Daten bereit sind.
|
||||
Die Aufbereitung kann einige Zeit dauern. Sie erhalten den Download-Link per E-Mail, sobald die Daten bereit sind.
|
||||
</div>
|
||||
<input type="email" id="email" name="email" class="form-control" required placeholder="name@firma.ch">
|
||||
<div class="invalid-feedback">Bitte eine gültige E-Mail Adresse eingeben.</div>
|
||||
<div class="invalid-feedback">Bitte geben Sie eine gültige E-Mail-Adresse ein.</div>
|
||||
</div>
|
||||
|
||||
<?php if ($turnstileSiteKey !== ''): ?>
|
||||
<div class="privacy-note mb-3">
|
||||
<div class="cf-turnstile"
|
||||
data-sitekey="<?php echo htmlspecialchars($turnstileSiteKey, ENT_QUOTES); ?>"
|
||||
data-action="turnstile-spin-v2"
|
||||
data-theme="auto"
|
||||
data-language="de"
|
||||
data-feedback-enabled="false"></div>
|
||||
<div class="form-text mt-2">
|
||||
Der Missbrauchsschutz verarbeitet technisch notwendige Browser- und Verbindungsdaten.
|
||||
Details finden Sie in der <a href="datenschutz.php">Datenschutzerklärung</a>.
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Der Export ist vorübergehend deaktiviert, da der Missbrauchsschutz noch nicht konfiguriert ist.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Export starten</button>
|
||||
<button type="submit" class="btn btn-primary btn-lg"<?php echo $turnstileSiteKey === '' ? ' disabled' : ''; ?>>Export starten</button>
|
||||
</div>
|
||||
<div id="selectionValidationMessage" class="text-danger mt-2" style="display:none;">
|
||||
Wählen Sie mindestens einen Ort und eine Rechtsform
|
||||
Wählen Sie mindestens einen Ort und eine Rechtsform aus.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -233,9 +312,11 @@ include "zefixAPI.php";
|
||||
input.type = "checkbox";
|
||||
input.name = checkboxName;
|
||||
input.value = checkboxValue;
|
||||
input.id = checkboxName.replace(/[^a-z0-9]/gi, '-') + '-' + String(checkboxValue).replace(/[^a-z0-9]/gi, '-');
|
||||
|
||||
const label = document.createElement('label');
|
||||
label.className = "form-check-label";
|
||||
label.htmlFor = input.id;
|
||||
label.textContent = labelText;
|
||||
|
||||
wrapper.appendChild(input);
|
||||
@@ -305,9 +386,11 @@ include "zefixAPI.php";
|
||||
input.type = "checkbox";
|
||||
input.name = "sitze[]";
|
||||
input.value = bfsId;
|
||||
input.id = "sitz-" + bfsId;
|
||||
|
||||
const label = document.createElement('label');
|
||||
label.className = "form-check-label";
|
||||
label.htmlFor = input.id;
|
||||
label.textContent = " " + gemeindeName;
|
||||
|
||||
wrapper.appendChild(input);
|
||||
@@ -319,6 +402,22 @@ include "zefixAPI.php";
|
||||
|
||||
const sitzInput = document.getElementById('sitzInput');
|
||||
|
||||
function updateSelectionCounts() {
|
||||
const cantonTotal = document.querySelectorAll('input[name="kantone[]"]:checked').length;
|
||||
const seatTotal = document.querySelectorAll('input[name="sitze[]"]:checked').length;
|
||||
const legalFormTotal = document.querySelectorAll('input[name="rechtsformen[]"]:checked').length;
|
||||
|
||||
document.getElementById('cantonCount').textContent = cantonTotal + ' ausgewählt';
|
||||
document.getElementById('seatCount').textContent = seatTotal + ' ausgewählt';
|
||||
document.getElementById('legalFormCount').textContent = legalFormTotal + ' ausgewählt';
|
||||
}
|
||||
|
||||
document.addEventListener('change', function(event) {
|
||||
if (event.target && event.target.matches('input[type="checkbox"]')) {
|
||||
updateSelectionCounts();
|
||||
}
|
||||
});
|
||||
|
||||
function kantoneAusgewählt() {
|
||||
const checkboxes = document.querySelectorAll('input[name="kantone[]"]');
|
||||
let countChecked = 0;
|
||||
@@ -374,6 +473,7 @@ include "zefixAPI.php";
|
||||
cb.checked = false;
|
||||
});
|
||||
filterFunction();
|
||||
updateSelectionCounts();
|
||||
}
|
||||
|
||||
function gefundeneGemeindenAuswählen() {
|
||||
@@ -383,19 +483,24 @@ include "zefixAPI.php";
|
||||
if (entry && entry.style.display !== "none") cb.checked = true;
|
||||
});
|
||||
filterFunction();
|
||||
updateSelectionCounts();
|
||||
}
|
||||
|
||||
function alleRechtsformenAuswählen() {
|
||||
document.querySelectorAll('input[name="rechtsformen[]"]').forEach(function(cb) {
|
||||
cb.checked = true;
|
||||
});
|
||||
updateSelectionCounts();
|
||||
}
|
||||
|
||||
function rechtsformenLöschen() {
|
||||
document.querySelectorAll('input[name="rechtsformen[]"]').forEach(function(cb) {
|
||||
cb.checked = false;
|
||||
});
|
||||
updateSelectionCounts();
|
||||
}
|
||||
|
||||
updateSelectionCounts();
|
||||
</script>
|
||||
|
||||
</div>
|
||||
@@ -405,7 +510,7 @@ include "zefixAPI.php";
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="border-top bg-white">
|
||||
<footer class="site-footer border-top">
|
||||
<div class="container py-4">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
@@ -416,7 +521,7 @@ include "zefixAPI.php";
|
||||
8245 Feuerthalen<br>
|
||||
<a href="https://www.silias.ch" target="_blank" rel="noopener">www.silias.ch</a><br>
|
||||
<a href="mailto:info@silias.ch">info@silias.ch</a><br>
|
||||
<a href="https://gitea.silias.ch/Silias-Public/Zefix_search">Projekt Repository</a>
|
||||
<a href="https://gitea.silias.ch/Silias-Public/Zefix_search">Projekt-Repository</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -435,23 +540,10 @@ include "zefixAPI.php";
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<h2 class="h6 mb-2">Warteliste</h2>
|
||||
<h2 class="h6 mb-2">Datenschutz</h2>
|
||||
<div class="text-muted" style="font-size:.95rem;">
|
||||
<script>
|
||||
const deleteFiles = function () {
|
||||
let taskDeletePassword = prompt("Passwort eingeben:");
|
||||
if (taskDeletePassword === null) return;
|
||||
let formdata = new FormData();
|
||||
formdata.append('taskDeletePassword', taskDeletePassword);
|
||||
fetch("deleteFiles.php", { method: "POST", body: formdata })
|
||||
.then(() => location.reload());
|
||||
}
|
||||
</script>
|
||||
|
||||
Momentan sind
|
||||
<b><?php $taskDir = 'tasks'; echo count(scandir($taskDir)) - 2 ?></b>
|
||||
Aufträge in der Warteliste.<br>
|
||||
<a href="#" onclick="deleteFiles(); return false;">alle löschen</a>
|
||||
Wir verwenden Ihre E-Mail-Adresse ausschliesslich zur Bereitstellung des angeforderten Exports.
|
||||
<br><a href="datenschutz.php">Datenschutzerklärung</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user