559 lines
23 KiB
PHP
559 lines
23 KiB
PHP
<?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">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
||
<link rel="stylesheet" href="css/bootstrap.css">
|
||
<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>
|
||
|
||
<style>
|
||
/* Nice input for the search box */
|
||
#sitzInput {
|
||
box-sizing: border-box;
|
||
font-size: 16px;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: .5rem;
|
||
padding: .6rem .8rem;
|
||
width: 100%;
|
||
}
|
||
|
||
.scrollWindow {
|
||
max-height: 320px;
|
||
overflow-y: auto;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: .75rem;
|
||
background: #fff;
|
||
padding: .75rem;
|
||
}
|
||
|
||
.button-small {
|
||
font-size: 0.8rem;
|
||
padding: .35rem .6rem;
|
||
border-radius: .5rem;
|
||
}
|
||
|
||
.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 site-header border-bottom py-2">
|
||
<div class="container">
|
||
<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>
|
||
|
||
<main class="container py-4 py-md-5">
|
||
<div class="row justify-content-center">
|
||
<div class="col-lg-10 col-xl-9">
|
||
|
||
<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">
|
||
|
||
<form action="submit.php" method="post" class="needs-validation" novalidate>
|
||
|
||
<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>
|
||
|
||
<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>
|
||
<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>
|
||
|
||
<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="Ort suchen"
|
||
id="sitzInput"
|
||
onkeyup="filterFunction()"
|
||
onchange="filterFunction()"
|
||
name="sitzfilter"
|
||
autocomplete="off">
|
||
</div>
|
||
|
||
<div class="d-flex flex-wrap gap-2">
|
||
<button type="button" class="btn btn-outline-primary button-small" onclick="gefundeneGemeindenAuswählen()">
|
||
Gefilterte Orte auswählen
|
||
</button>
|
||
<button type="button" class="btn btn-outline-secondary button-small" onclick="ausgewählteGemeindenLöschen()">
|
||
Auswahl löschen
|
||
</button>
|
||
<button type="button" class="btn btn-outline-secondary button-small" onclick="alleGemeindenAnzeigen()">
|
||
Alle anzeigen
|
||
</button>
|
||
<button type="button" class="btn btn-outline-secondary button-small" onclick="nurAusgewählteGemeindenAnzeigen()">
|
||
Nur ausgewählte anzeigen
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="sitzauswahl" class="scrollWindow"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<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="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>
|
||
|
||
<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>
|
||
</div>
|
||
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" id="geloeschteRechtseinheiten" name="geloeschteRechtseinheiten">
|
||
<label class="form-check-label" for="geloeschteRechtseinheiten">Gelöschte Rechtseinheiten suchen</label>
|
||
</div>
|
||
|
||
<div class="form-check">
|
||
<input class="form-check-input" type="checkbox" id="inBisherigenFirmen" name="inBisherigenFirmen" disabled>
|
||
<label class="form-check-label" for="inBisherigenFirmen">In bisherigen Firmen/Namen suchen (noch in Entwicklung)</label>
|
||
</div>
|
||
|
||
<div class="form-check">
|
||
<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 class="mb-4">
|
||
<label for="email" class="form-label">Ihre E-Mail-Adresse</label>
|
||
<div class="form-text mb-2">
|
||
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 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"<?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 aus.
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</form>
|
||
|
||
<script>
|
||
// Bootstrap validation (optional)
|
||
(function () {
|
||
'use strict'
|
||
const forms = document.querySelectorAll('.needs-validation')
|
||
Array.prototype.slice.call(forms).forEach(function (form) {
|
||
form.addEventListener('submit', function (event) {
|
||
const hasSelectedSitz = document.querySelector('input[name="sitze[]"]:checked') !== null
|
||
const hasSelectedRechtsform = document.querySelector('input[name="rechtsformen[]"]:checked') !== null
|
||
const selectionValidationMessage = document.getElementById('selectionValidationMessage')
|
||
|
||
if (selectionValidationMessage) {
|
||
selectionValidationMessage.style.display = hasSelectedSitz && hasSelectedRechtsform ? 'none' : 'block'
|
||
}
|
||
|
||
if (!form.checkValidity() || !hasSelectedSitz || !hasSelectedRechtsform) {
|
||
event.preventDefault()
|
||
event.stopPropagation()
|
||
}
|
||
form.classList.add('was-validated')
|
||
}, false)
|
||
})
|
||
})();
|
||
</script>
|
||
|
||
<script>
|
||
// CSV data injected by PHP
|
||
let communityCsvData;
|
||
let legalFormsCsvData;
|
||
<?php
|
||
include 'gemeinden.php';
|
||
include 'legalForms.php';
|
||
?>
|
||
|
||
// --- Helpers to create nicer checkbox rows (Bootstrap form-check) ---
|
||
function addCheckboxRow(container, checkboxName, checkboxValue, labelText) {
|
||
const wrapper = document.createElement('div');
|
||
wrapper.className = "form-check";
|
||
|
||
const input = document.createElement('input');
|
||
input.className = "form-check-input";
|
||
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);
|
||
wrapper.appendChild(label);
|
||
container.appendChild(wrapper);
|
||
|
||
return input;
|
||
}
|
||
|
||
// --- Legal forms ---
|
||
const legalFormslines = legalFormsCsvData.trim().split('\n');
|
||
const legalFormsheaders = legalFormslines[0].split(',');
|
||
const legalformIdIndex = legalFormsheaders.indexOf('id');
|
||
const legalformNameIndex = legalFormsheaders.indexOf('name');
|
||
|
||
const rechtsformenauswahl = document.getElementById("rechtsformenauswahl");
|
||
for (let i = 1; i < legalFormslines.length; i++) {
|
||
const cells = legalFormslines[i].split(',');
|
||
const id = (cells[legalformIdIndex] || '').trim();
|
||
const name = (cells[legalformNameIndex] || '').trim();
|
||
if (!id || !name) continue;
|
||
addCheckboxRow(rechtsformenauswahl, "rechtsformen[]", id, " " + name);
|
||
}
|
||
|
||
// --- Communities + Cantons ---
|
||
const lines = communityCsvData.trim().split('\n');
|
||
const headers = lines[0].split(',');
|
||
const gemeindeNameIndex = headers.indexOf('Gemeindename');
|
||
const bfsIdIndex = headers.indexOf('id');
|
||
const kantonIndex = headers.indexOf('Kanton');
|
||
const registryOfCommerceIdIndex = headers.indexOf('registryOfCommerceId');
|
||
|
||
const sitze = [];
|
||
const kantone = [];
|
||
const kantonauswahl = document.getElementById("kantonauswahl");
|
||
const sitzauswahl = document.getElementById("sitzauswahl");
|
||
|
||
for (let i = 1; i < lines.length; i++) {
|
||
const cells = lines[i].split(',');
|
||
if (!cells || cells.length < 4) continue;
|
||
|
||
const bfsId = (cells[bfsIdIndex] || '').trim();
|
||
const gemeindeName = (cells[gemeindeNameIndex] || '').trim();
|
||
const kanton = (cells[kantonIndex] || '').trim();
|
||
const registryOfCommerceId = (cells[registryOfCommerceIdIndex] || '').trim();
|
||
|
||
if (!bfsId || !gemeindeName) continue;
|
||
|
||
// Add canton if new
|
||
if (kanton && !kantone.includes(kanton)) {
|
||
kantone.push(kanton);
|
||
addCheckboxRow(kantonauswahl, "kantone[]", kanton, " " + kanton);
|
||
}
|
||
|
||
// Store row for filtering logic
|
||
sitze.push(cells);
|
||
|
||
// Create nicer entry
|
||
const gemeindeEintrag = document.createElement("div");
|
||
gemeindeEintrag.classList.add("gemeindeeintrag");
|
||
|
||
const wrapper = document.createElement('div');
|
||
wrapper.className = "form-check";
|
||
|
||
const input = document.createElement('input');
|
||
input.className = "form-check-input";
|
||
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);
|
||
wrapper.appendChild(label);
|
||
gemeindeEintrag.appendChild(wrapper);
|
||
|
||
sitzauswahl.appendChild(gemeindeEintrag);
|
||
}
|
||
|
||
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;
|
||
for (let i = 0; i < checkboxes.length; i++) {
|
||
if (checkboxes[i].checked) countChecked++;
|
||
}
|
||
return countChecked;
|
||
}
|
||
|
||
function filterFunction() {
|
||
const numKantoneAusgewählt = kantoneAusgewählt();
|
||
const filter = (sitzInput.value || "").toUpperCase();
|
||
const entries = sitzauswahl.getElementsByClassName("gemeindeeintrag");
|
||
|
||
for (let i = 0; i < entries.length; i++) {
|
||
const txtValue = entries[i].textContent || entries[i].innerText;
|
||
|
||
const selectedCheckbox = entries[i].querySelector('input[type="checkbox"]');
|
||
const isSelected = selectedCheckbox ? selectedCheckbox.checked : false;
|
||
|
||
// map entry->sitz row index: still aligned because we push sitze once per entry
|
||
const row = sitze[i];
|
||
const rowKanton = row ? row[kantonIndex] : "";
|
||
|
||
let cantonOk = true;
|
||
if (numKantoneAusgewählt > 0 && rowKanton) {
|
||
const cantonBox = document.querySelector('input[name="kantone[]"][value="' + rowKanton + '"]');
|
||
cantonOk = cantonBox ? cantonBox.checked : false;
|
||
}
|
||
|
||
const matchesText = txtValue.toUpperCase().indexOf(filter) > -1;
|
||
|
||
if ((matchesText && (cantonOk || numKantoneAusgewählt === 0)) || isSelected) {
|
||
entries[i].style.display = "";
|
||
} else {
|
||
entries[i].style.display = "none";
|
||
}
|
||
}
|
||
}
|
||
|
||
function alleGemeindenAnzeigen() {
|
||
sitzInput.value = "";
|
||
filterFunction();
|
||
}
|
||
|
||
function nurAusgewählteGemeindenAnzeigen() {
|
||
sitzInput.value = "(nur ausgewählte)";
|
||
filterFunction();
|
||
}
|
||
|
||
function ausgewählteGemeindenLöschen() {
|
||
sitzauswahl.querySelectorAll('input[type="checkbox"]').forEach(function(cb) {
|
||
cb.checked = false;
|
||
});
|
||
filterFunction();
|
||
updateSelectionCounts();
|
||
}
|
||
|
||
function gefundeneGemeindenAuswählen() {
|
||
sitzauswahl.querySelectorAll('input[type="checkbox"]').forEach(function(cb) {
|
||
// show/hide is on the gemeindeeintrag wrapper
|
||
const entry = cb.closest('.gemeindeeintrag');
|
||
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>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<footer class="site-footer border-top">
|
||
<div class="container py-4">
|
||
<div class="row g-4">
|
||
<div class="col-md-4">
|
||
<h2 class="h6 mb-2">Impressum</h2>
|
||
<div class="text-muted" style="font-size:.95rem;">
|
||
Silias KLG<br>
|
||
Toggenburgstrasse 31<br>
|
||
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>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<h2 class="h6 mb-2">Datenquelle</h2>
|
||
<div class="text-muted" style="font-size:.95rem;">
|
||
Zentraler Firmenindex<br>
|
||
Eidgenössisches Justiz- und Polizeidepartement (EJPD)<br>
|
||
Bundesamt für Justiz (BJ)<br>
|
||
Eidgenössisches Amt für das Handelsregister (EHRA)<br>
|
||
Bundesrain 20, 3003 Bern<br>
|
||
<a href="https://www.zefix.admin.ch" target="_blank" rel="noopener">www.zefix.admin.ch</a><br>
|
||
<a href="tel:+41584624197">+41 (0) 58 462 41 97</a><br>
|
||
<a href="mailto:zefix@bj.admin.ch">zefix@bj.admin.ch</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<h2 class="h6 mb-2">Datenschutz</h2>
|
||
<div class="text-muted" style="font-size:.95rem;">
|
||
Wir verwenden Ihre E-Mail-Adresse ausschliesslich zur Bereitstellung des angeforderten Exports.
|
||
<br><a href="datenschutz.php">Datenschutzerklärung</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="text-center text-muted mt-4" style="font-size:.9rem;">
|
||
© <?php echo date('Y'); ?> Silias KLG
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
</body>
|
||
</html>
|