docs: README auf reine Nutzersicht umstellen (Funktion & Nutzung, ohne Internas)
This commit is contained in:
@@ -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. 1–5) | 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`.*
|
|
||||||
|
|||||||
Reference in New Issue
Block a user