Compare commits

..

No commits in common. "main" and "v1.0.0" have entirely different histories.
main ... v1.0.0

8098 changed files with 1042617 additions and 8960 deletions

View file

@ -18,10 +18,9 @@ APP_MAINTENANCE_DRIVER=file
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=daily
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=warning
LOG_DAILY_DAYS=14
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1

44
.gitignore vendored
View file

@ -1,44 +0,0 @@
# ── Dependencies ──
/node_modules/
/vendor/
/schneespur/node_modules/
/schneespur/vendor/
# ── Local env (keep .env.example tracked) ──
.env
.env.*
!.env.example
# ── Laravel runtime ──
/schneespur/storage/logs/*.log
/schneespur/storage/framework/cache/*
/schneespur/storage/framework/sessions/*
/schneespur/storage/framework/views/*
/schneespur/storage/testing/
# ── Build artifacts ──
/schneespur/public/build/
/schneespur/public/hot
/release/
# ── Internal dev files (not part of distributable app) ──
/build.sh
/moduldoku.md
/package-lock.json
# ── Test caches ──
.phpunit.result.cache
.phpunit.cache/
# ── IDE / OS ──
.idea/
.vscode/
*.swp
.DS_Store
Thumbs.db
# ── Local tooling ──
.gsd
.gsd-id
.mcp.json
.bg-shell/

View file

@ -27,9 +27,9 @@ Diese Anleitung beschreibt die Installation von Schneespur auf einem klassischen
| MySQL | 5.7 | 8.0+ |
| MariaDB (alternativ) | 10.3 | 10.6+ |
### Benötigte PHP-Erweiterungen
### Benoetigte PHP-Erweiterungen
**Pflicht** (Installation schlägt ohne diese fehl):
**Pflicht** (Installation schlaegt ohne diese fehl):
- `pdo_mysql`
- `gd`
@ -89,9 +89,9 @@ Das Document-Root (manchmal auch „Webroot" oder „Stammverzeichnis" genannt)
**Beispiel:** Wenn Sie die Dateien nach `/schneespur/` hochgeladen haben, setzen Sie das Document-Root auf `/schneespur/public/`.
So geht das bei gängigen Hostern:
So geht das bei gaengigen Hostern:
- **Strato:** Paket-Verwaltung → Domain-Verwaltung → Umleitung/Ziel → Pfad ändern
- **Strato:** Paket-Verwaltung → Domain-Verwaltung → Umleitung/Ziel → Pfad aendern
- **IONOS:** Hosting → Domains → Document-Root bearbeiten
- **All-Inkl:** Domain-Einstellungen → Ordnerzuordnung
@ -101,7 +101,7 @@ So geht das bei gängigen Hostern:
## 4. Datenbank anlegen
Erstellen Sie über das Verwaltungspanel Ihres Hosters eine neue MySQL-Datenbank. Notieren Sie sich:
Erstellen Sie ueber das Verwaltungspanel Ihres Hosters eine neue MySQL-Datenbank. Notieren Sie sich:
- **Host** (z. B. `localhost` oder `rdbms.strato.de`)
- **Port** (Standard: `3306`)
@ -109,27 +109,27 @@ Erstellen Sie über das Verwaltungspanel Ihres Hosters eine neue MySQL-Datenbank
- **Benutzername**
- **Passwort**
Diese Daten benötigen Sie im nächsten Schritt.
Diese Daten benoetigen Sie im naechsten Schritt.
---
## 5. Installations-Assistent
Öffnen Sie Ihre Domain im Browser. Schneespur erkennt automatisch, dass noch keine Installation vorliegt, und startet den Assistenten.
Oeffnen Sie Ihre Domain im Browser. Schneespur erkennt automatisch, dass noch keine Installation vorliegt, und startet den Assistenten.
### Schritt 1: Willkommen
Der Assistent prüft die Grundvoraussetzungen und erzeugt die Konfigurationsdatei (`.env`) sowie den Anwendungsschlüssel (`APP_KEY`).
Der Assistent prueft die Grundvoraussetzungen und erzeugt die Konfigurationsdatei (`.env`) sowie den Anwendungsschluessel (`APP_KEY`).
### Schritt 2: Datenbank
Geben Sie die Zugangsdaten aus Schritt 4 ein. Der Assistent testet die Verbindung, bevor er fortfährt.
Geben Sie die Zugangsdaten aus Schritt 4 ein. Der Assistent testet die Verbindung, bevor er fortfaehrt.
> Falls die `.env`-Datei nicht beschreibbar ist (selten bei Shared-Hosting), zeigt der Assistent eine Anleitung zum manuellen Bearbeiten per FTP an.
### Schritt 3: Systemcheck
Der Assistent prüft PHP-Version, Erweiterungen und Schreibrechte auf wichtige Verzeichnisse (`storage/`, `bootstrap/cache/`). Fehlende Erweiterungen werden als Pflicht oder Empfehlung markiert.
Der Assistent prueft PHP-Version, Erweiterungen und Schreibrechte auf wichtige Verzeichnisse (`storage/`, `bootstrap/cache/`). Fehlende Erweiterungen werden als Pflicht oder Empfehlung markiert.
### Schritt 4: Datenbank-Migration
@ -145,7 +145,7 @@ Legen Sie fest:
### Schritt 6: Speicher & Caches
Der Assistent erstellt die Verknüpfung zum öffentlichen Speicher (`storage:link`) und baut Caches auf. Falls die Verknüpfung auf Ihrem Hoster nicht funktioniert, wird eine Anleitung zum manuellen Anlegen per FTP angezeigt.
Der Assistent erstellt die Verknuepfung zum oeffentlichen Speicher (`storage:link`) und baut Caches auf. Falls die Verknuepfung auf Ihrem Hoster nicht funktioniert, wird eine Anleitung zum manuellen Anlegen per FTP angezeigt.
### Schritt 7: Admin-Konto
@ -153,17 +153,17 @@ Erstellen Sie Ihr Administrator-Konto (Name, E-Mail, Passwort mit mindestens 8 Z
### Schritt 8: E-Mail-Konfiguration (optional)
Richten Sie SMTP-Versand ein, damit Schneespur Benachrichtigungen senden kann. Dieser Schritt kann übersprungen und später in den Einstellungen nachgeholt werden.
Richten Sie SMTP-Versand ein, damit Schneespur Benachrichtigungen senden kann. Dieser Schritt kann uebersprungen und spaeter in den Einstellungen nachgeholt werden.
### Fertig
Nach Abschluss sehen Sie eine Zusammenfassung. Sie können sich jetzt mit Ihren Admin-Zugangsdaten anmelden.
Nach Abschluss sehen Sie eine Zusammenfassung. Sie koennen sich jetzt mit Ihren Admin-Zugangsdaten anmelden.
---
## 6. Cron-Job einrichten
Schneespur benötigt einen Cron-Job, der einmal pro Minute den Laravel-Scheduler ausführt. Dieser verarbeitet die Auftragswarteschlange (z. B. Wetterdaten abrufen, Benachrichtigungen senden).
Schneespur benoetigt einen Cron-Job, der einmal pro Minute den Laravel-Scheduler ausfuehrt. Dieser verarbeitet die Auftragswarteschlange (z. B. Wetterdaten abrufen, Benachrichtigungen senden).
### Cron-Befehl
@ -171,7 +171,7 @@ Schneespur benötigt einen Cron-Job, der einmal pro Minute den Laravel-Scheduler
* * * * * /usr/local/bin/php /pfad/zu/schneespur/artisan schedule:run >> /dev/null 2>&1
```
> **Wichtig:** Ersetzen Sie `/pfad/zu/schneespur/` durch den tatsächlichen Pfad auf Ihrem Webspace und `/usr/local/bin/php` durch den PHP-Pfad Ihres Hosters (häufig auch `/usr/bin/php` oder `/usr/bin/php8.3`).
> **Wichtig:** Ersetzen Sie `/pfad/zu/schneespur/` durch den tatsaechlichen Pfad auf Ihrem Webspace und `/usr/local/bin/php` durch den PHP-Pfad Ihres Hosters (haeufig auch `/usr/bin/php` oder `/usr/bin/php8.3`).
### So richten Sie den Cron-Job ein
@ -179,13 +179,13 @@ Schneespur benötigt einen Cron-Job, der einmal pro Minute den Laravel-Scheduler
- **IONOS:** Hosting → Cron-Jobs → Cronjob anlegen
- **All-Inkl:** Tools → Cronjobs → Neuer Cronjob
Stellen Sie die Ausführung auf **jede Minute** oder das kürzeste verfügbare Intervall.
Stellen Sie die Ausfuehrung auf **jede Minute** oder das kuerzeste verfuegbare Intervall.
### Warum ist der Cron-Job nötig?
### Warum ist der Cron-Job noetig?
Ohne Cron-Job werden keine Hintergrundaufgaben verarbeitet:
- Wetterdaten werden nicht automatisch zu Einsätzen hinzugefügt
- Wetterdaten werden nicht automatisch zu Einsaetzen hinzugefuegt
- E-Mail-Benachrichtigungen werden nicht versendet
- Geplante Aufgaben laufen nicht
@ -193,18 +193,18 @@ Ohne Cron-Job werden keine Hintergrundaufgaben verarbeitet:
## 7. OwnTracks einrichten
OwnTracks ist die GPS-Tracking-App, mit der Ihre Fahrer die Einsätze aufzeichnen. Jeder Fahrer benötigt die App auf seinem Smartphone.
OwnTracks ist die GPS-Tracking-App, mit der Ihre Fahrer die Einsaetze aufzeichnen. Jeder Fahrer benoetigt die App auf seinem Smartphone.
### Kurzanleitung
1. **App installieren:** OwnTracks aus dem App Store (iOS) oder Google Play Store (Android) herunterladen.
2. **Zugangsdaten erzeugen:** Melden Sie sich als Admin in Schneespur an, öffnen Sie die Fahrer-Übersicht und klicken Sie beim jeweiligen Fahrer auf „Zugangsdaten". Schneespur erzeugt automatisch Benutzername und Passwort.
2. **Zugangsdaten erzeugen:** Melden Sie sich als Admin in Schneespur an, oeffnen Sie die Fahrer-Uebersicht und klicken Sie beim jeweiligen Fahrer auf „Zugangsdaten". Schneespur erzeugt automatisch Benutzername und Passwort.
3. **QR-Code scannen:** Auf der Zugangsdaten-Seite wird ein QR-Code angezeigt. Der Fahrer scannt diesen mit der OwnTracks-App, und die Verbindung wird automatisch konfiguriert.
4. **Manuell konfigurieren** (falls QR-Code nicht funktioniert):
- Modus: **HTTP**
- URL: `https://ihre-domain.de/api/owntracks/report`
- Benutzername und Passwort: wie in Schneespur angezeigt
5. **Testen:** Öffnen Sie in Schneespur unter „OwnTracks" die Übersicht. Sobald der Fahrer die App startet, sollte dort ein grüner Status erscheinen.
5. **Testen:** Oeffnen Sie in Schneespur unter „OwnTracks" die Uebersicht. Sobald der Fahrer die App startet, sollte dort ein gruener Status erscheinen.
---
@ -213,17 +213,17 @@ OwnTracks ist die GPS-Tracking-App, mit der Ihre Fahrer die Einsätze aufzeichne
### Vor dem Update
1. Erstellen Sie ein Backup (siehe [Backup](#9-backup)).
2. Aktivieren Sie den Wartungsmodus: Öffnen Sie `https://ihre-domain.de/down` im Browser oder führen Sie `php artisan down` per SSH/Cron aus.
2. Aktivieren Sie den Wartungsmodus: Oeffnen Sie `https://ihre-domain.de/down` im Browser oder fuehren Sie `php artisan down` per SSH/Cron aus.
### Update durchführen
### Update durchfuehren
1. Laden Sie das neue Release herunter.
2. Überschreiben Sie alle Dateien per FTP. Überspringen Sie dabei **nicht** die `.env`-Datei — diese wird beim Upload ohnehin nicht überschrieben, solange Sie nur die Release-Dateien hochladen.
3. Führen Sie die Datenbank-Migration aus. Dafür gibt es zwei Wege:
- **Ueber den Browser:** Öffnen Sie `https://ihre-domain.de/admin/settings` und prüfen Sie, ob eine Update-Migration angeboten wird.
2. Ueberschreiben Sie alle Dateien per FTP. Ueberspringen Sie dabei **nicht** die `.env`-Datei — diese wird beim Upload ohnehin nicht ueberschrieben, solange Sie nur die Release-Dateien hochladen.
3. Fuehren Sie die Datenbank-Migration aus. Dafuer gibt es zwei Wege:
- **Ueber den Browser:** Oeffnen Sie `https://ihre-domain.de/admin/settings` und pruefen Sie, ob eine Update-Migration angeboten wird.
- **Per Cron/SSH:** `php artisan migrate --force`
4. Leeren Sie die Caches: `php artisan config:cache && php artisan view:cache`
5. Deaktivieren Sie den Wartungsmodus: Öffnen Sie `https://ihre-domain.de/up` oder führen Sie `php artisan up` aus.
5. Deaktivieren Sie den Wartungsmodus: Oeffnen Sie `https://ihre-domain.de/up` oder fuehren Sie `php artisan up` aus.
---
@ -239,9 +239,9 @@ OwnTracks ist die GPS-Tracking-App, mit der Ihre Fahrer die Einsätze aufzeichne
### Empfohlener Rhythmus
- **Datenbank:** wöchentlich oder vor jedem Update
- **Datenbank:** woechentlich oder vor jedem Update
- **Dateien:** vor jedem Update
- **Konfiguration:** nach jeder Änderung und vor Updates
- **Konfiguration:** nach jeder Aenderung und vor Updates
---
@ -249,47 +249,47 @@ OwnTracks ist die GPS-Tracking-App, mit der Ihre Fahrer die Einsätze aufzeichne
### Installations-Assistent erscheint nicht
- Prüfen Sie, ob das Document-Root korrekt auf `/public` zeigt.
- Prüfen Sie, ob die `.htaccess`-Datei im `public/`-Ordner vorhanden ist.
- Pruefen Sie, ob das Document-Root korrekt auf `/public` zeigt.
- Pruefen Sie, ob die `.htaccess`-Datei im `public/`-Ordner vorhanden ist.
- Stellen Sie sicher, dass `mod_rewrite` (Apache) aktiviert ist.
### Datenbankverbindung schlägt fehl
### Datenbankverbindung schlaegt fehl
- Prüfen Sie Host, Port, Datenbankname, Benutzername und Passwort.
- Pruefen Sie Host, Port, Datenbankname, Benutzername und Passwort.
- Bei Strato lautet der Host oft `rdbms.strato.de`, nicht `localhost`.
- Stellen Sie sicher, dass der Datenbankbenutzer Zugriff auf die angegebene Datenbank hat.
### Seite zeigt „500 Internal Server Error"
- Prüfen Sie die Schreibrechte: `storage/` und `bootstrap/cache/`ssen beschreibbar sein (Rechte 755 oder 775).
- Pruefen Sie die Schreibrechte: `storage/` und `bootstrap/cache/` muessen beschreibbar sein (Rechte 755 oder 775).
- Schauen Sie in `storage/logs/laravel.log` nach der Fehlermeldung.
### GPS-Daten kommen nicht an
- Prüfen Sie in OwnTracks, ob der Modus auf „HTTP" steht (nicht MQTT).
- Prüfen Sie die URL: `https://ihre-domain.de/api/owntracks/report`
- Prüfen Sie Benutzername und Passwort in der OwnTracks-App.
- Öffnen Sie die OwnTracks-Übersicht in Schneespur — dort wird der letzte Verbindungsstatus angezeigt.
- Pruefen Sie in OwnTracks, ob der Modus auf „HTTP" steht (nicht MQTT).
- Pruefen Sie die URL: `https://ihre-domain.de/api/owntracks/report`
- Pruefen Sie Benutzername und Passwort in der OwnTracks-App.
- Oeffnen Sie die OwnTracks-Uebersicht in Schneespur — dort wird der letzte Verbindungsstatus angezeigt.
### Wetterdaten fehlen bei Einsätzen
### Wetterdaten fehlen bei Einsaetzen
- Stellen Sie sicher, dass der Cron-Job läuft (siehe [Cron-Job einrichten](#6-cron-job-einrichten)).
- Wetterdaten werden über Open-Meteo abgerufen. Prüfen Sie, ob Ihr Server ausgehende HTTPS-Verbindungen erlaubt.
- Stellen Sie sicher, dass der Cron-Job laeuft (siehe [Cron-Job einrichten](#6-cron-job-einrichten)).
- Wetterdaten werden ueber Open-Meteo abgerufen. Pruefen Sie, ob Ihr Server ausgehende HTTPS-Verbindungen erlaubt.
### E-Mails werden nicht versendet
- Prüfen Sie die SMTP-Einstellungen unter Einstellungen → E-Mail.
- Pruefen Sie die SMTP-Einstellungen unter Einstellungen → E-Mail.
- Nutzen Sie die Test-E-Mail-Funktion in den Einstellungen.
- Schauen Sie in `storage/logs/laravel.log` nach Fehlermeldungen.
### Cron-Job funktioniert nicht
- Prüfen Sie den PHP-Pfad: Führen Sie `which php` aus oder fragen Sie Ihren Hoster.
- Prüfen Sie den Pfad zur `artisan`-Datei.
- Pruefen Sie den PHP-Pfad: Fuehren Sie `which php` aus oder fragen Sie Ihren Hoster.
- Pruefen Sie den Pfad zur `artisan`-Datei.
- Testen Sie den Befehl manuell: `php /pfad/zu/schneespur/artisan schedule:run`
---
## Hilfe
Bei Fragen nutzen Sie die integrierte Hilfe im Admin-Bereich (Menü → Hilfe) oder erstellen Sie ein Issue im GitHub-Repository.
Bei Fragen nutzen Sie die integrierte Hilfe im Admin-Bereich (Menue → Hilfe) oder erstellen Sie ein Issue im GitHub-Repository.

View file

@ -1,5 +1,5 @@
<p align="center">
<img src="schneespur/public/pwa-icon-512x512.png" alt="Schneespur" width="120">
<img src="public/pwa-icon-512x512.png" alt="Schneespur" width="120">
</p>
<h1 align="center">Schneespur</h1>
@ -9,39 +9,34 @@
GPS-Tracks &middot; Wetterdaten &middot; Fotos &middot; rechtsfester Einsatznachweis
</p>
<p align="center">
<a href="https://schneespur.de">schneespur.de</a> &middot;
<a href="https://wintertrace.com">wintertrace.com</a>
</p>
<p align="center">
<a href="#english">English</a> &middot;
<a href="INSTALL.de.md">Installation (DE)</a> &middot;
<a href="INSTALL.en.md">Installation (EN)</a> &middot;
<a href="https://schneespur.de/download/">Download</a>
<a href="https://jenni.noschmarrn.dev">Download</a>
</p>
---
## Was ist Schneespur?
Schneespur dokumentiert Räum- und Streueinsätze für kleine Winterdienst-Betriebe — vollständig, automatisch und rechtssicher. Die Software läuft auf jedem günstigen Shared-Webhosting (Strato, IONOS, All-Inkl, ...) und braucht weder SSH noch Docker.
Schneespur dokumentiert Raeum- und Streueinsaetze fuer kleine Winterdienst-Betriebe — vollstaendig, automatisch und rechtssicher. Die Software laeuft auf jedem guenstigen Shared-Webhosting (Strato, IONOS, All-Inkl, ...) und braucht weder SSH noch Docker.
**Kernversprechen:** Wenn ein Passant auf einer gestreuten Fläche ausrutscht und der Betreiber nachweisen muss, dass er seiner Verkehrssicherungspflicht nachgekommen ist, liefert Schneespur den Beleg — mit GPS-Track, Wetterlage, Fotos und Zeitstempeln.
**Kernversprechen:** Wenn ein Passant auf einer gestreuten Flaeche ausrutscht und der Betreiber nachweisen muss, dass er seiner Verkehrssicherungspflicht nachgekommen ist, liefert Schneespur den Beleg — mit GPS-Track, Wetterlage, Fotos und Zeitstempeln.
### Funktionen
- **GPS-Tracking** via [OwnTracks](https://owntracks.org)-App (iOS/Android) — kein eigener Tracking-Client nötig
- **GPS-Tracking** via [OwnTracks](https://owntracks.org)-App (iOS/Android) — kein eigener Tracking-Client noetig
- **Automatische Wetterdokumentation** — Temperatur, Niederschlag, Wind, Schneelage zum Einsatzzeitpunkt (Open-Meteo, BrightSky, Met.no)
- **Foto-Dokumentation** — Bilder direkt aus der Fahrer-App hochladen
- **PDF-Einsatznachweise** — einzeln oder als Sammelreport pro Kunde und Zeitraum
- **Kundenportal** — Kunden können ihre Einsätze selbst einsehen
- **Kundenportal** — Kunden koennen ihre Einsaetze selbst einsehen
- **Fahrer-App (PWA)** — funktioniert offline, synchronisiert automatisch bei Verbindung
- **Kunden- und Objektverwaltung** — mehrere Objekte pro Kunde, Zuordnung zu Einsätzen
- **Kunden- und Objektverwaltung** — mehrere Objekte pro Kunde, Zuordnung zu Einsaetzen
- **Fahrzeugverwaltung** — Fuhrpark mit Kennzeichen und Fahrzeugtyp
- **DSGVO-konform** — Fahrer-Anonymisierung, Datenexport, konfigurierbare Aufbewahrungsfristen
- **Automatische Updates** — kryptographisch signiert (Ed25519), ein Klick im Admin-Panel
- **Modulsystem**erweiterbar über Module aus dem Schneespur-Modulkatalog
- **Modulsystem**Erweiterbar ueber Module aus dem Schneespur-Modulkatalog
### Systemanforderungen
@ -55,10 +50,10 @@ Schneespur dokumentiert Räum- und Streueinsätze für kleine Winterdienst-Betri
### Schnellstart
1. [Download](https://schneespur.de/download/) der aktuellen Version (ZIP)
1. [Download](https://jenni.noschmarrn.dev) der aktuellen Version (ZIP)
2. ZIP entpacken und per FTP auf den Webserver laden
3. Document Root auf den `public/`-Ordner setzen
4. Im Browser die Domain aufrufen — der Installations-Assistent führt durch die Einrichtung
4. Im Browser die Domain aufrufen — der Installations-Assistent fuehrt durch die Einrichtung
Detaillierte Anleitung: **[INSTALL.de.md](INSTALL.de.md)**
@ -115,7 +110,7 @@ Schneespur (German) / Wintertrace (international) is an open-source, self-hosted
### Quick Start
1. [Download](https://wintertrace.com/download/) the latest release (ZIP)
1. [Download](https://jenni.noschmarrn.dev) the latest release (ZIP)
2. Extract and upload via FTP to your web server
3. Set the document root to the `public/` directory
4. Open the domain in your browser — the installation wizard guides you through setup

1
VERSION Normal file
View file

@ -0,0 +1 @@
1.0.0

View file

@ -4,7 +4,6 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Module;
use App\Services\ModuleManager;
use App\Services\SchneespurModuleClient;
use App\Services\SchneespurModuleInstaller;
use Illuminate\Http\RedirectResponse;
@ -23,7 +22,12 @@ class AdminModuleController extends Controller
try {
$catalog = $client->fetchCatalog();
if ($catalog !== null) {
$catalogModules = $catalog['modules'] ?? [];
} else {
$state = $client->loadState();
$catalogModules = $state['installed'] ?? [];
}
} catch (\Throwable $e) {
Log::warning('schneespur-modules: catalog fetch failed in admin UI', [
'error' => $e->getMessage(),
@ -54,7 +58,6 @@ class AdminModuleController extends Controller
'download_url' => $catModule['download_url'] ?? null,
'sha256' => $catModule['sha256'] ?? null,
'size_bytes' => $catModule['size_bytes'] ?? null,
'requires_permissions' => $catModule['requires_permissions'] ?? [],
];
}
@ -77,7 +80,6 @@ class AdminModuleController extends Controller
'download_url' => null,
'sha256' => null,
'size_bytes' => null,
'requires_permissions' => $this->resolveLocalPermissions($slug),
];
}
@ -228,16 +230,6 @@ class AdminModuleController extends Controller
->with('success', __('modules.disabled', ['slug' => $slug]));
}
private function resolveLocalPermissions(string $slug): array
{
try {
$manager = app(ModuleManager::class);
return $manager->getPermissions($slug);
} catch (\Throwable) {
return [];
}
}
public function remove(string $slug, SchneespurModuleInstaller $installer): RedirectResponse
{
$module = Module::where('slug', $slug)->first();

View file

@ -5,7 +5,6 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Setting;
use App\Services\Extension\DashboardWidgetRegistry;
use App\Services\Extension\FilterRegistry;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
@ -18,10 +17,9 @@ class DashboardController extends Controller
return redirect()->route('admin.dashboard');
}
public function index(DashboardWidgetRegistry $widgetRegistry, FilterRegistry $filterRegistry): View
public function index(DashboardWidgetRegistry $widgetRegistry): View
{
$widgets = $widgetRegistry->getWidgets();
$widgets = $filterRegistry->apply('schneespur.dashboard.kpis', $widgets);
return view('admin.dashboard', compact('widgets'));
}

View file

@ -3,7 +3,6 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\DsgvoOnboardingController;
use App\Models\DsgvoConfirmation;
use App\Models\Setting;
use Illuminate\Http\Request;
@ -19,7 +18,7 @@ class DsgvoAdminController extends Controller
$version = (int) Setting::get('dsgvo_template_version', 1);
if ($markdown === null) {
$markdown = view(DsgvoOnboardingController::resolveDefaultTemplateView())->render();
$markdown = view('dsgvo.default-template')->render();
}
$previewHtml = Str::markdown($this->replacePlaceholders($markdown), ['html_input' => 'strip']);
@ -83,7 +82,25 @@ class DsgvoAdminController extends Controller
private function replacePlaceholders(string $text): string
{
return dsgvo_apply_company_placeholders($text);
$companyName = Setting::get('company_name', '');
$street = Setting::get('company_street', '');
$zip = Setting::get('company_zip', '');
$city = Setting::get('company_city', '');
$email = Setting::get('company_email', '');
$dpo = Setting::get('dpo_contact', '');
$dpoEmail = Setting::get('dpo_email', '');
$address = trim("$street, $zip $city", ', ');
$replacements = [
'[Firmenname eintragen]' => $companyName ?: '[Firmenname eintragen]',
'[Adresse eintragen]' => $address ?: '[Adresse eintragen]',
'[E-Mail-Adresse eintragen]' => $email ?: '[E-Mail-Adresse eintragen]',
'[DPO-E-Mail-Adresse eintragen]' => $dpoEmail ?: '[DPO-E-Mail-Adresse eintragen]',
'[Datenschutzbeauftragter / Ansprechpartner eintragen]' => $dpo ?: '[Datenschutzbeauftragter / Ansprechpartner eintragen]',
];
return str_replace(array_keys($replacements), array_values($replacements), $text);
}
public function showConfirmation(int $id): View

View file

@ -66,22 +66,32 @@ class DsgvoOnboardingController extends Controller
$version = (int) Setting::get('dsgvo_template_version', 1);
if ($text === null) {
$text = view(self::resolveDefaultTemplateView())->render();
$text = view('dsgvo.default-template')->render();
}
return [$text, $version];
}
public static function resolveDefaultTemplateView(): string
{
$locale = app()->getLocale();
$localized = "dsgvo.default-template-{$locale}";
return view()->exists($localized) ? $localized : 'dsgvo.default-template';
}
private function replacePlaceholders(string $text): string
{
return dsgvo_apply_company_placeholders($text);
$companyName = Setting::get('company_name', '');
$street = Setting::get('company_street', '');
$zip = Setting::get('company_zip', '');
$city = Setting::get('company_city', '');
$email = Setting::get('company_email', '');
$dpo = Setting::get('dpo_contact', '');
$dpoEmail = Setting::get('dpo_email', '');
$address = trim("$street, $zip $city", ', ');
$replacements = [
'[Firmenname eintragen]' => $companyName ?: '[Firmenname eintragen]',
'[Adresse eintragen]' => $address ?: '[Adresse eintragen]',
'[E-Mail-Adresse eintragen]' => $email ?: '[E-Mail-Adresse eintragen]',
'[DPO-E-Mail-Adresse eintragen]' => $dpoEmail ?: '[DPO-E-Mail-Adresse eintragen]',
'[Datenschutzbeauftragter / Ansprechpartner eintragen]' => $dpo ?: '[Datenschutzbeauftragter / Ansprechpartner eintragen]',
];
return str_replace(array_keys($replacements), array_values($replacements), $text);
}
}

View file

@ -27,17 +27,6 @@ class InstallerController extends Controller
private InstallLockManager $lockManager,
) {}
// --- Locale switcher (works on any installer step) ---
public function switchLocale(Request $request, string $locale): RedirectResponse
{
if (in_array($locale, ['de', 'en'], true)) {
$request->session()->put('installer_locale', $locale);
}
return redirect($request->headers->get('referer') ?: route('install.welcome'));
}
// --- Step 1: Welcome ---
public function showWelcome(Request $request): View
@ -129,7 +118,7 @@ class InstallerController extends Controller
{
if ($this->preflightChecker->hasCriticalFailures()) {
return redirect()->route('install.preflight')
->withErrors(['preflight' => __('install.preflight_has_failures')]);
->withErrors(['preflight' => 'Kritische Voraussetzungen nicht erfüllt.']);
}
return redirect()->route('install.database');

View file

@ -19,14 +19,6 @@ class StoreCustomerObjectRequest extends FormRequest
'price_amount' => str_replace(',', '.', $this->price_amount),
]);
}
// notify_recipients is a mode enum consumed as customer|object|both
// (see SendJobCompletedNotification / SendCustomerReportEmail).
// Fall back to the DB default when the field is missing or empty
// so the NOT NULL column never receives a null value.
if (! $this->filled('notify_recipients')) {
$this->merge(['notify_recipients' => 'customer']);
}
}
/**
@ -52,7 +44,7 @@ class StoreCustomerObjectRequest extends FormRequest
'lon' => ['nullable', 'numeric', 'between:-180,180'],
'auto_notify_email' => ['boolean'],
'notification_email' => ['nullable', 'required_if:auto_notify_email,1', 'email', 'max:200'],
'notify_recipients' => ['required', 'in:customer,object,both'],
'notify_recipients' => ['nullable', 'string', 'max:1000'],
];
}

Some files were not shown because too many files have changed in this diff Show more