docs: README auf reine Nutzersicht umstellen (Funktion & Nutzung, ohne Internas)

This commit is contained in:
KAI
2026-06-16 16:53:36 +02:00
parent d73c49dc3d
commit b86be43ed9
+57 -256
View File
@@ -1,286 +1,87 @@
# MeinVLN Beteiligungsportal # MeinVLN Beteiligungsportal
Token-basiertes **Beteiligungsportal** des VLN Manager. Behörden, Träger öffentlicher Das Beteiligungsportal macht **Anhörungen und Stellungnahmen digital** statt per Post.
Belange (TöB) und Einzelpersonen erhalten über einen persönlichen Zugriffscode **ohne Behörden, Träger öffentlicher Belange (TöB) und einzelne Beteiligte bekommen einen
Login** Einsicht in bereitgestellte Dokumente und können einen Fragenkatalog **persönlichen Link** und können darüber **ohne Anmeldung, ohne Konto** die
beantworten. Jeder Seitenaufruf und jeder Dateidownload wird revisionssicher bereitgestellten Unterlagen einsehen, herunterladen und einen Fragenkatalog
protokolliert. beantworten.
Erreichbar unter **https://beteiligung.vlnsachsen.de** (eigener nginx-Container - **Empfänger** öffnen ihren Link auf **beteiligung.vlnsachsen.de** und antworten.
`nginx-beteiligung`). Angelegt, gepflegt und ausgewertet werden Verfahren im - **Teilnehmergemeinschaften und Verbandsmitarbeitende** legen die Verfahren an und
geschützten Self-Service-Portal **mein.vlnsachsen.de** sowie über die **REST-API v2**. werten sie aus im Portal **mein.vlnsachsen.de**.
> **Hinweis:** Dieses Repository dokumentiert das Beteiligungsmodul. Der produktive
> Code lebt im Monorepo des VLN Manager und verteilt sich auf mehrere Verzeichnisse
> (siehe [Codebestandteile](#codebestandteile)).
--- ---
## Inhalt ## Was kann das Portal?
- [Was es ist & wofür](#was-es-ist--wofür) - Unterlagen gezielt an einen Kreis von Empfängern freigeben
- [Rollen & Ablauf](#rollen--ablauf) - Jedem Empfänger einen eigenen, persönlichen Zugangslink geben
- [Codebestandteile](#codebestandteile) - Fragen stellen (Ja/Nein, Auswahl, Freitext, Zahl, Datum, Bewertung)
- [Datenmodell](#datenmodell) - Eine Frist setzen, bis zu der geantwortet werden kann
- [Fragenkatalog](#fragenkatalog) - Auswerten, wer geantwortet hat, was geantwortet wurde und wer die Unterlagen
- [REST-API v2](#rest-api-v2) angesehen/heruntergeladen hat
- [Berechtigungen](#berechtigungen)
- [Betrieb & Deployment](#betrieb--deployment)
- [Sicherheitshinweise](#sicherheitshinweise)
--- ---
## Was es ist & wofür ## Ein Verfahren anlegen und betreuen *(TG-Vorsitz, Sachbearbeitung, Verband)*
Im Flurbereinigungsverfahren müssen regelmäßig Behörden und Beteiligte angehört werden. Alles passiert im Portal **mein.vlnsachsen.de** unter „Beteiligung".
Das Beteiligungsportal löst dafür den klassischen Postweg ab:
- Eine **Teilnehmergemeinschaft (TG)** oder ein Verbandsmitarbeiter legt ein 1. **Verfahren anlegen** Titel (worum geht es) und Frist (bis wann) festlegen.
**Beteiligungsverfahren** an (Titel, Frist, VKZ). 2. **Empfänger hinzufügen** Anrede, Name und E-Mail erfassen. Für jeden Empfänger
- Pro **Empfänger** wird ein eindeutiger, kryptographisch sicherer **Zugriffscode** wird automatisch ein eigener, persönlicher Link erzeugt.
erzeugt (`uniqidReal()`, 13 Zeichen, Großbuchstaben-Hex). 3. **Fragen anlegen** beliebig viele Fragen, je nach Bedarf Pflicht oder freiwillig
- Der Empfänger öffnet seinen Link `…/?code=<CODE>`, lädt die freigegebenen Dokumente (Fragetypen siehe unten).
(einzeln oder als ZIP) herunter und beantwortet den Fragenkatalog. 4. **Unterlagen hochladen** die Dokumente, die die Empfänger sehen sollen.
- Die TG wertet Rückläufe und Zugriffe aus. 5. **Links versenden** jeden persönlichen Link an den jeweiligen Empfänger schicken.
6. **Auswerten** jederzeit sehen, wer schon geantwortet hat, was geantwortet wurde
und wer wann zugegriffen oder heruntergeladen hat.
Kein Empfänger-Account, kein Passwort der **Code ist das Token**. Codes werden niemals > Hinweis: Jeder Empfänger sieht nur seinen eigenen Link und seine eigenen Antworten.
geraten (Zufallsquelle `random_bytes`/`openssl_random_pseudo_bytes`).
--- ---
## Rollen & Ablauf ## Als Empfänger teilnehmen
| Rolle | Ort | Aktion | 1. **Link öffnen** der persönliche Link führt direkt zur Beteiligungsseite, ohne
|-------|-----|--------| Anmeldung.
| **Verband / TG-Vorsitz / TG-Sachbearbeiter** | `mein.vlnsachsen.de` | Verfahren anlegen, Empfänger/Fragen/Dateien pflegen, auswerten | 2. **Unterlagen ansehen** einzelne Dokumente herunterladen oder alle zusammen als
| **Empfänger** (Behörde, TöB, Person) | `beteiligung.vlnsachsen.de/?code=…` | Dokumente einsehen/herunterladen, Fragen beantworten | ZIP-Datei.
| **System / Integration** | `api.…/v2/beteiligung` | Verfahren per API anlegen, Public-Read/Submit per Token | 3. **Fragen beantworten** das Formular ausfüllen und absenden.
4. **Frist beachten** nach Ablauf der Frist ist keine Abgabe mehr möglich.
``` Bereits abgegebene Antworten werden beim erneuten Öffnen angezeigt und können bis zum
Anlegen (meinVLN) Beteiligung (öffentlich) Auswertung (meinVLN) Ende der Frist noch geändert werden.
───────────────── ───────────────────────── ────────────────────
beteiligung_create.php ──► index.php?code=<TOKEN> ──► beteiligung_auswertung.php
beteiligung_edit.php ├─ Dateien (Einzel/ZIP) ├─ Antworten + Aggregate
├─ Stammdaten └─ antwort.php (Fragen) └─ Zugriffsverlauf (Audit)
├─ Empfänger (→ Code)
├─ Fragen
└─ Dateien
```
--- ---
## Codebestandteile ## Fragetypen
| Pfad | Zweck | | Typ | Bedeutung | Antwort durch den Empfänger |
|------|-------| |-----|-----------|------------------------------|
| `0_beteiligung/index.php` | **Public-Entry**: Datei-/ZIP-Download, Seite mit Fragenformular | | **Einfachauswahl** | genau eine Option wählen (z. B. Ja/Nein) | ein Punkt |
| `0_beteiligung/antwort.php` | **Public-Submit**: speichert Antworten (Pflichtfeld-Validierung, Typ-Normalisierung) | | **Mehrfachauswahl** | mehrere Optionen ankreuzen | beliebig viele Punkte |
| `0_beteiligung/templates/` | Mustache-Templates des Public-Portals (`login`, `main`, `header`, `footer`) | | **Freitext** | offene Antwort | Text |
| `0_beteiligung/set.php` | Hilfsskript: listet Upload-Verzeichnis (Debug) | | **Zahl** | eine Zahl | Zahleneingabe |
| `require/require-beteiligungsverfahren.php` | **Domänenklasse** `BETEILIGUNGSVERFAHREN` + Factory `get_BETEILIGUNGSVERFAHREN()` | | **Datum** | ein Datum | Datumsauswahl |
| `0_meinvln/beteiligung_create.php` | Verfahren anlegen (JSON-Endpoint) | | **Bewertung (Skala)** | Einstufung auf einer Skala (z. B. 15) | ein Wert auf der Skala |
| `0_meinvln/beteiligung_edit.php` | CRUD-Dispatcher (`?action=meta\|empfaenger_*\|frage_save\|frage_del\|datei_upload\|datei_del\|verfahren_del`) |
| `0_meinvln/beteiligung_auswertung.php` | Antworten-Auswertung + Zugriffsverlauf |
| `0_meinvln/templates/beteiligung_{edit,auswertung}.mustache` | Admin-Oberflächen |
| `0_api/v2/plugins/beteiligung.php` | **REST-API v2** (List/Create + Public Read/Submit) |
Anlegen erfolgt zusätzlich über ein Modal in `0_meinvln/templates/meinvln_main.mustache`, Jede Frage kann als **Pflichtfrage** markiert werden dann lässt sich das Formular
das auf `beteiligung_create.php` postet. erst absenden, wenn sie beantwortet ist.
--- ---
## Datenmodell ## Gut zu wissen
Zwei Tabellen, keine ORM rohes MySQLi über den `DB`-Wrapper. - **Der Link ist persönlich.** Er ersetzt Login und Passwort also nicht öffentlich
weitergeben.
### `BETEILIGUNGSVERFAHREN` - **Kein Konto nötig.** Empfänger brauchen weder Registrierung noch Software,
nur einen Browser.
| Spalte | Typ | Bedeutung | - **Die Frist ist verbindlich.** Nach Ablauf können Unterlagen nicht mehr abgerufen
|--------|-----|-----------| und keine Antworten mehr abgegeben werden.
| `id` | INT, PK, auto | Verfahrens-ID | - **Nachvollziehbar.** In der Auswertung ist ersichtlich, wer die Seite geöffnet,
| `VKZ` | INT | Verfahrenskennzeichen (TG-Zuordnung) | Dokumente heruntergeladen und wann geantwortet hat.
| `von` | INT (Unix-TS) | Beginn des Beteiligungszeitraums |
| `bis` | INT (Unix-TS) | Frist (Zugriff/Antwort enden 23:59:59 dieses Tages) |
| `CODES` | JSON | Empfänger inkl. Code, Stammdaten, Antworten |
| `DATEN` | JSON | Array der freigegebenen Datei-IDs |
| `SETTING` | JSON | Titel, Ersteller, **Fragenkatalog** |
Indizes: `idx_vkz`, `idx_bis`, `idx_vkz_bis`.
**`CODES`** Map `Code → Empfänger`:
```json
{
"A1B2C3D4E5F60": {
"name": "Landratsamt Musterkreis",
"anrede": "Damen und Herren",
"email": "post@lra-muster.de",
"z": [],
"antworten": { "f1": "Ja", "f2": ["A", "C"], "f3": 4 },
"antwort_ts": 1718524800
}
}
```
**`SETTING`**:
```json
{
"titel": "Anhörung Wegebau Gewann Süd",
"created_by": 62,
"created_at": 1718438400,
"fragen": [ /* siehe Fragenkatalog */ ]
}
```
**`DATEN`**: `["10231", "10232", "10240"]` IDs aus dem zentralen Dateisystem
(`DATEI`/`ORDNER`). Physische Ablage im Ordnerbaum unter `<VKZ>/Beteiligung/<verfahren_id>`.
### `BETEILIGUNGSVERFAHREN_ZUGRIFF` (Audit-Log)
Append-only, race-condition-frei ein `INSERT` pro Zugriff. Ersetzt das frühere
JSON-Logging in `CODES[code].{a,z}` (seit 2026-05-23; **nie wieder** Zugriffslogs in die
JSON-Spalte schreiben).
| Spalte | Typ | Bedeutung |
|--------|-----|-----------|
| `verfahren_id` | INT | FK auf `BETEILIGUNGSVERFAHREN.id` |
| `code` | VARCHAR | Empfänger-Code |
| `typ` | ENUM(`page`,`file`,`zip`) | Art des Zugriffs |
| `file_id` | INT NULL | bei `typ=file` die Datei-ID |
| `ts` | INT (Unix-TS) | Zeitpunkt |
| `ip` | VARBINARY | via `INET6_ATON()` (IPv4/IPv6) |
| `user_agent` | VARCHAR(255) | gekürzter UA-String |
Aggregation für die Auswertung über `BETEILIGUNGSVERFAHREN::get_zugriffsstats()`
(GROUP BY `code, typ`), Detailverlauf per JOIN auf `DATA_FILE` in
`beteiligung_auswertung.php`.
--- ---
## Fragenkatalog *Teil des VLN Manager Flurneuordnung Sachsen.*
Fragen liegen in `SETTING.fragen`. Jede Frage hat eine stabile ID (`f1`, `f2`, …),
einen Typ und ein `pflicht`-Flag. Erlaubte Typen:
| Typ | Bedeutung | Zusatzfelder | Antwortformat |
|-----|-----------|--------------|---------------|
| `single` | Einfachauswahl | `optionen` (≥ 2) | String (eine Option) |
| `multi` | Mehrfachauswahl | `optionen` (≥ 2) | String-Array |
| `text` | Freitext | | String |
| `zahl` | Zahl | | Number |
| `datum` | Datum | | parsebarer Datums-String |
| `skala` | Skala | `min`, `max` (`max > min`) | Integer in `[min, max]` |
```json
[
{ "id": "f1", "typ": "single", "frage": "Bestehen Einwände?",
"pflicht": true, "optionen": ["Ja", "Nein"] },
{ "id": "f2", "typ": "multi", "frage": "Betroffene Belange",
"optionen": ["Natur", "Wasser", "Verkehr"] },
{ "id": "f3", "typ": "skala", "frage": "Dringlichkeit", "min": 1, "max": 5 }
]
```
Eingehende Antworten werden **serverseitig gegen den Katalog validiert** (Optionen
müssen existieren, Skala im Bereich, Pflichtfragen vorhanden) sowohl in `antwort.php`
als auch im API-Endpoint.
---
## REST-API v2
Basis: `https://api.…/v2/beteiligung`. Admin-Routen erfordern einen API-Key
(`auth: true`), Public-Routen nutzen den Empfänger-Token.
| Methode | Route | Zugriff | Zweck |
|---------|-------|---------|-------|
| `GET` | `/beteiligung` | Admin | Verfahren listen (`?vkz=`, `?status=aktiv\|abgelaufen\|alle`) |
| `POST` | `/beteiligung` | Admin | Verfahren anlegen (inkl. Empfänger + Fragen) |
| `GET` | `/beteiligung/{token}` | Public | Verfahren + Dateien + Fragen + bisherige Antwort |
| `POST` | `/beteiligung/{token}/antworten` | Public | Antworten speichern (Frist + Validierung) |
| `POST` | `/beteiligung/{id}/empfaenger` | Admin | Weiteren Empfänger anhängen |
**Verfahren anlegen:**
```bash
curl -X POST https://api.…/v2/beteiligung \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"vkz": 1234,
"bis": "2026-07-31",
"empfaenger": [
{ "name": "Landratsamt Musterkreis", "anrede": "Damen und Herren", "email": "post@lra-muster.de" }
],
"fragen": [
{ "typ": "single", "frage": "Einwände?", "pflicht": true, "optionen": ["Ja","Nein"] }
]
}'
```
**Antworten einreichen (öffentlich, per Token):**
```bash
curl -X POST https://api.…/v2/beteiligung/A1B2C3D4E5F60/antworten \
-H "Content-Type: application/json" \
-d '{ "antworten": { "f1": "Nein" } }'
```
Fehlercodes: `400` (ungültige Eingabe), `404` (Token/Verfahren unbekannt),
`410` (Frist abgelaufen), `422` (Pflichtfragen fehlen).
---
## Berechtigungen
- **Admin-Seiten** (`mein.vlnsachsen.de`): Modul-Gate `MODUL_BETEILIGUNG`
(`$RIGHT->darf_modul(...)`) Verbandsmitarbeiter sowie freigeschaltete
TG-Vorsitzende/Stellvertreter.
- **Pro Verfahren** zusätzlich `tg_access($VKZ)`: TG-Nutzer sehen nur Verfahren
*ihrer* VKZ, der Verband sieht alle.
- **Public-Portal**: keine Anmeldung; Autorisierung allein über den Code in `CODES`.
Dateien werden nur über `jumpfile()`/`jumpzip()` ausgeliefert (kein direkter
Pfadzugriff), Downloads vor der Auslieferung protokolliert.
---
## Betrieb & Deployment
Eigener nginx-Container, getrennt von den anderen Subdomains.
- **docker-compose:** `/daten/dockervorlagen/manager/docker-compose.beteiligung.yaml`
(Service `nginxbeteiligung`, Container `nginx-beteiligung`, Routing via Traefik auf
`beteiligung.vlnsachsen.de`, TLS über `myresolver`).
- **nginx-Conf:** `/daten/dockervorlagen/manager/nginx_beteiligung.conf`
(Webroot `0_beteiligung/`, PHP-FPM `phpworker:9000`).
- **URL-Konstante:** `URL_BETEILIGUNG = "https://beteiligung.vlnsachsen.de"`
(in `require/require-main.php`).
```bash
# Container (neu) starten
cd /daten/dockervorlagen/manager
docker compose -f docker-compose.beteiligung.yaml up -d
# PHP-Syntaxcheck (PHP läuft nur im Container)
docker exec php-worker php -l /var/www/html/require/require-beteiligungsverfahren.php
```
---
## Sicherheitshinweise
- **Codes sind Geheimnisse.** Ein Code gewährt vollen Lese-/Antwortzugriff auf ein
Verfahren nur über sichere Kanäle versenden.
- **Zugriffslogs gehören in die Audit-Tabelle** `BETEILIGUNGSVERFAHREN_ZUGRIFF`,
nie zurück in die `CODES`-JSON-Spalte.
- **Frist (`bis`) ist hart**: Nach Ablauf verweigert die API das Speichern (`410 Gone`).
- **Eingaben werden serverseitig validiert** Client-Werte (Optionen, Skala, Pflicht)
nie ungeprüft übernehmen.
- **Dateiauslieferung nur indirekt** über die Domänenklasse; keine Pfade aus
Client-Input.
---
*Teil des VLN Manager (Flurbereinigung Sachsen). Domänenklasse:
`require/require-beteiligungsverfahren.php`.*