diff --git a/README.md b/README.md index a5d8ff6..cc1874f 100644 --- a/README.md +++ b/README.md @@ -1,286 +1,87 @@ # MeinVLN – Beteiligungsportal -Token-basiertes **Beteiligungsportal** des VLN Manager. Behörden, Träger öffentlicher -Belange (TöB) und Einzelpersonen erhalten über einen persönlichen Zugriffscode – **ohne -Login** – Einsicht in bereitgestellte Dokumente und können einen Fragenkatalog -beantworten. Jeder Seitenaufruf und jeder Dateidownload wird revisionssicher -protokolliert. +Das Beteiligungsportal macht **Anhörungen und Stellungnahmen digital** statt per Post. +Behörden, Träger öffentlicher Belange (TöB) und einzelne Beteiligte bekommen einen +**persönlichen Link** und können darüber – **ohne Anmeldung, ohne Konto** – die +bereitgestellten Unterlagen einsehen, herunterladen und einen Fragenkatalog +beantworten. -Erreichbar unter **https://beteiligung.vlnsachsen.de** (eigener nginx-Container -`nginx-beteiligung`). Angelegt, gepflegt und ausgewertet werden Verfahren im -geschützten Self-Service-Portal **mein.vlnsachsen.de** sowie über die **REST-API v2**. - -> **Hinweis:** Dieses Repository dokumentiert das Beteiligungsmodul. Der produktive -> Code lebt im Monorepo des VLN Manager und verteilt sich auf mehrere Verzeichnisse -> (siehe [Codebestandteile](#codebestandteile)). +- **Empfänger** öffnen ihren Link auf **beteiligung.vlnsachsen.de** und antworten. +- **Teilnehmergemeinschaften und Verbandsmitarbeitende** legen die Verfahren an und + werten sie aus – im Portal **mein.vlnsachsen.de**. --- -## Inhalt +## Was kann das Portal? -- [Was es ist & wofür](#was-es-ist--wofür) -- [Rollen & Ablauf](#rollen--ablauf) -- [Codebestandteile](#codebestandteile) -- [Datenmodell](#datenmodell) -- [Fragenkatalog](#fragenkatalog) -- [REST-API v2](#rest-api-v2) -- [Berechtigungen](#berechtigungen) -- [Betrieb & Deployment](#betrieb--deployment) -- [Sicherheitshinweise](#sicherheitshinweise) +- Unterlagen gezielt an einen Kreis von Empfängern freigeben +- Jedem Empfänger einen eigenen, persönlichen Zugangslink geben +- Fragen stellen (Ja/Nein, Auswahl, Freitext, Zahl, Datum, Bewertung) +- Eine Frist setzen, bis zu der geantwortet werden kann +- Auswerten, wer geantwortet hat, was geantwortet wurde und wer die Unterlagen + angesehen/heruntergeladen hat --- -## 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. -Das Beteiligungsportal löst dafür den klassischen Postweg ab: +Alles passiert im Portal **mein.vlnsachsen.de** unter „Beteiligung". -- Eine **Teilnehmergemeinschaft (TG)** oder ein Verbandsmitarbeiter legt ein - **Beteiligungsverfahren** an (Titel, Frist, VKZ). -- Pro **Empfänger** wird ein eindeutiger, kryptographisch sicherer **Zugriffscode** - erzeugt (`uniqidReal()`, 13 Zeichen, Großbuchstaben-Hex). -- Der Empfänger öffnet seinen Link `…/?code=`, lädt die freigegebenen Dokumente - (einzeln oder als ZIP) herunter und beantwortet den Fragenkatalog. -- Die TG wertet Rückläufe und Zugriffe aus. +1. **Verfahren anlegen** – Titel (worum geht es) und Frist (bis wann) festlegen. +2. **Empfänger hinzufügen** – Anrede, Name und E-Mail erfassen. Für jeden Empfänger + wird automatisch ein eigener, persönlicher Link erzeugt. +3. **Fragen anlegen** – beliebig viele Fragen, je nach Bedarf Pflicht oder freiwillig + (Fragetypen siehe unten). +4. **Unterlagen hochladen** – die Dokumente, die die Empfänger sehen sollen. +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 -geraten (Zufallsquelle `random_bytes`/`openssl_random_pseudo_bytes`). +> Hinweis: Jeder Empfänger sieht nur seinen eigenen Link und seine eigenen Antworten. --- -## Rollen & Ablauf +## Als Empfänger teilnehmen -| Rolle | Ort | Aktion | -|-------|-----|--------| -| **Verband / TG-Vorsitz / TG-Sachbearbeiter** | `mein.vlnsachsen.de` | Verfahren anlegen, Empfänger/Fragen/Dateien pflegen, auswerten | -| **Empfänger** (Behörde, TöB, Person) | `beteiligung.vlnsachsen.de/?code=…` | Dokumente einsehen/herunterladen, Fragen beantworten | -| **System / Integration** | `api.…/v2/beteiligung` | Verfahren per API anlegen, Public-Read/Submit per Token | +1. **Link öffnen** – der persönliche Link führt direkt zur Beteiligungsseite, ohne + Anmeldung. +2. **Unterlagen ansehen** – einzelne Dokumente herunterladen oder alle zusammen als + ZIP-Datei. +3. **Fragen beantworten** – das Formular ausfüllen und absenden. +4. **Frist beachten** – nach Ablauf der Frist ist keine Abgabe mehr möglich. -``` -Anlegen (meinVLN) Beteiligung (öffentlich) Auswertung (meinVLN) -───────────────── ───────────────────────── ──────────────────── -beteiligung_create.php ──► index.php?code= ──► beteiligung_auswertung.php -beteiligung_edit.php ├─ Dateien (Einzel/ZIP) ├─ Antworten + Aggregate - ├─ Stammdaten └─ antwort.php (Fragen) └─ Zugriffsverlauf (Audit) - ├─ Empfänger (→ Code) - ├─ Fragen - └─ Dateien -``` +Bereits abgegebene Antworten werden beim erneuten Öffnen angezeigt und können bis zum +Ende der Frist noch geändert werden. --- -## Codebestandteile +## Fragetypen -| Pfad | Zweck | -|------|-------| -| `0_beteiligung/index.php` | **Public-Entry**: Datei-/ZIP-Download, Seite mit Fragenformular | -| `0_beteiligung/antwort.php` | **Public-Submit**: speichert Antworten (Pflichtfeld-Validierung, Typ-Normalisierung) | -| `0_beteiligung/templates/` | Mustache-Templates des Public-Portals (`login`, `main`, `header`, `footer`) | -| `0_beteiligung/set.php` | Hilfsskript: listet Upload-Verzeichnis (Debug) | -| `require/require-beteiligungsverfahren.php` | **Domänenklasse** `BETEILIGUNGSVERFAHREN` + Factory `get_BETEILIGUNGSVERFAHREN()` | -| `0_meinvln/beteiligung_create.php` | Verfahren anlegen (JSON-Endpoint) | -| `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) | +| Typ | Bedeutung | Antwort durch den Empfänger | +|-----|-----------|------------------------------| +| **Einfachauswahl** | genau eine Option wählen (z. B. Ja/Nein) | ein Punkt | +| **Mehrfachauswahl** | mehrere Optionen ankreuzen | beliebig viele Punkte | +| **Freitext** | offene Antwort | Text | +| **Zahl** | eine Zahl | Zahleneingabe | +| **Datum** | ein Datum | Datumsauswahl | +| **Bewertung (Skala)** | Einstufung auf einer Skala (z. B. 1–5) | ein Wert auf der Skala | -Anlegen erfolgt zusätzlich über ein Modal in `0_meinvln/templates/meinvln_main.mustache`, -das auf `beteiligung_create.php` postet. +Jede Frage kann als **Pflichtfrage** markiert werden – dann lässt sich das Formular +erst absenden, wenn sie beantwortet ist. --- -## Datenmodell +## Gut zu wissen -Zwei Tabellen, keine ORM – rohes MySQLi über den `DB`-Wrapper. - -### `BETEILIGUNGSVERFAHREN` - -| Spalte | Typ | Bedeutung | -|--------|-----|-----------| -| `id` | INT, PK, auto | Verfahrens-ID | -| `VKZ` | INT | Verfahrenskennzeichen (TG-Zuordnung) | -| `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 `/Beteiligung/`. - -### `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`. +- **Der Link ist persönlich.** Er ersetzt Login und Passwort – also nicht öffentlich + weitergeben. +- **Kein Konto nötig.** Empfänger brauchen weder Registrierung noch Software, + nur einen Browser. +- **Die Frist ist verbindlich.** Nach Ablauf können Unterlagen nicht mehr abgerufen + und keine Antworten mehr abgegeben werden. +- **Nachvollziehbar.** In der Auswertung ist ersichtlich, wer die Seite geöffnet, + Dokumente heruntergeladen und wann geantwortet hat. --- -## Fragenkatalog - -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 " \ - -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`.* +*Teil des VLN Manager – Flurneuordnung Sachsen.*