Entfernt, weil kein Code mehr darauf verwies: - smarty3/ (Smarty 3.1.6; alle Aufrufer nutzen smarty/ mit 4.5.6) - incs/tcpdf_old/ - incs/fpdf.php samt FPDF-Schriftmetriken in incs/font/*.php und incs/font/makefont/ (nur von sites/makepdf_OLD.php genutzt, parste ab PHP 8.0 ohnehin nicht mehr) - sites/makepdf_OLD.php - incs/users_old.php - oldindex.php (nirgends verlinkter Alt-Einstiegspunkt) Behalten: incs/PHPMailer_v5.1 (wird von sites/projectadd.php genutzt) und die TTFs in incs/font (werden von stats.php fuer pChart geladen). CLAUDE.md entsprechend nachgezogen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7.1 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Kundenverwaltung für ein Ingenieurbüro: Kunden, Termine, Projekte, Angebote, Lieferscheine, Rechnungen, Zeiterfassung. Gewachsene PHP-Anwendung (seit ~2011) mit Smarty-Templates und MariaDB/MySQL. Domänenbegriffe, Bezeichner und UI-Texte sind durchgehend deutsch — das bitte beibehalten.
Tooling
Es gibt kein Composer, npm, Build, Linter oder Testsuite. Abhängigkeiten liegen als Kopie in incs/ und werden dort direkt gepflegt. Ein Update einer dieser Bibliotheken ist immer ein manueller Eingriff, kein Paketmanager-Aufruf.
Syntaxprüfung der eigenen Dateien:
for f in $(git ls-files "*.php" | grep -vE "^(incs/(tcpdf|pChart|PHPMailer|WebAuthn|drawer|font)|smarty|spellcheck|js)/"); do php -l "$f"; done
Zum Ausführen braucht es einen Webserver mit PHP, eine MariaDB/MySQL-Instanz und die externe Konfigurationsdatei (siehe unten). Ohne die Konfiguration springt index.php in den Setup-Modus (update/firststart.php).
Die zwei zentralen Mechanismen
1. Das Verzeichnis bestimmt die Instanz
incs/constants.php leitet aus getcwd() den Namen des Installationsverzeichnisses ab und baut daraus:
| Konstante | Wert bei Installation in …/kunden |
|---|---|
CONFIG_FILE |
kunden_settings.conf |
UPLOADS_PATH |
uploads_kunden/ |
SESSION_NAME |
KUNDENSESS |
Aus einer Codebasis laufen dadurch mehrere getrennte Instanzen — die uploads_dev, uploads_ingwa, uploads_kunden, uploads_lsg im Baum sind die Spuren davon. Jede Instanz hat eigene Config, eigene Session, eigene Uploads.
Konsequenz für alles, was per CLI/Cron läuft: Das Arbeitsverzeichnis muss das Installationsverzeichnis sein, sonst wird die falsche (oder keine) Konfiguration geladen.
cd /home/intranet/kunden && php db-backup.php
constants.php kompensiert dabei nur die Aufrufe aus ajax/ und ajax/auto/, indem es ein bzw. drei Ebenen hochgeht.
2. Zugangsdaten liegen außerhalb des Repos
CONFIG_PATH zeigt auf /volume1/homes/wagner/kundenconfig/ (Linux) bzw. c:/kundenverwaltung/config/ (Windows). Geschrieben wird die Datei von update/rebuild-settings.php beim Ersteinrichten.
Die Datei deklariert $mysql_user, $mysql_pw, $mysql_server, $mysql_db, $firma und weitere ausdrücklich als global und weist sie erst danach zu. Nur deshalb funktioniert readsettings() in incs/settings.php: Das include passiert im Funktionsrumpf, ohne die global-Zeilen blieben die Variablen dort lokal. Wer die Config-Struktur ändert, muss diese Eigenheit erhalten.
Zugangsdaten gehören nie in den Code — db-backup.php zeigt das Muster für Skripte außerhalb des Request-Zyklus (Config einlesen, Passwort über eine temporäre Optionsdatei an mysqldump, nicht per Kommandozeile).
Request-Ablauf
index.php ist der einzige Einstieg für die Oberfläche:
- Konstanten, Session, Konfiguration, DB-Verbindung
- Schema-Migration (siehe unten)
head.tpl- bei gültiger Session:
checkRightsNInclude()ausincs/funcs.php— einswitchüber$_GET["action"], das Rechte prüft und den Pfad insites/zurückgibt, der dann inkludiert wird foot.tpl
Eine neue Seite anzulegen heißt: Datei in sites/ plus ein case in checkRightsNInclude(). Ohne den case ist die Seite nicht erreichbar.
Die Endpunkte in ajax/ gehen nicht über index.php. Sie bootstrappen selbst über incs/connectmysql.php (Konstanten, Session, Config, DB, Escape-Wrapper) und rendern eigene Fragmente nach ajax/templates_c/.
Rechtesystem
$_SESSION["rights"] ist ein String, keine Zahl. Die Zeichenposition benennt den Bereich, der Zeichenwert ist eine Bitmaske:
- Position:
RIGHTS_KUNDEN=0,RIGHTS_TERMINE=1,RIGHTS_ANGEBOTE=2,RIGHTS_LIEFER=3,RIGHTS_RECHNUNGEN=4,RIGHTS_SUPERADMIN=5 - Wert:
RIGHTS_LOOK=1,RIGHTS_CHANGE=2,RIGHTS_ADD=4
Geprüft wird immer nach diesem Muster:
if(substr($_SESSION["rights"], RIGHTS_KUNDEN, 1) & RIGHTS_LOOK)
Schema-Migrationen laufen bei jedem Seitenaufruf
Es gibt keine SQL-Migrationsdateien. Der Stand steht in update/thisversion/currentversion.txt (dreistellig, aktuell 010), die Zielversion ist der erste Eintrag in $versions in update/versions.php. index.php ruft bei jedem Request checknewversion() und manageupdate() aus incs/update.php; liegt die Zielversion höher, arbeitet update_tables() in update/database-update.php sich in einem switch von Version zu Version hoch und schreibt die neue Nummer zurück.
Eine Schemaänderung besteht also aus: neuer Eintrag vorn in update/versions.php und passender case in update_tables(). update/thisversion/ muss für den Webserver schreibbar sein.
Datenbankzugriff
Prozedurales mysqli. $dbconn wird zusätzlich in $_SESSION["dbconn"] abgelegt, damit incs/mysql_ecape_wrapper.php eine globale Funktion mysql_escape_string() als Shim über diese Verbindung bereitstellen kann — ein Überbleibsel der mysql_*-Ära, das in altem Code noch aufgerufen wird.
SQL wird durchgehend per String-Verkettung gebaut. Escaping ist Handarbeit über mysqli_real_escape_string($dbconn, …) und muss bei jeder Änderung mitgedacht werden.
Fallstricke
smarty/enthält Smarty 4.5.6 — der schlichte Verzeichnisname sagt nichts über die Version.- PHPMailer liegt in zwei Versionen parallel und beide sind aktiv:
index.phplädtincs/PHPMailer_v6für den Rechnungsversand,sites/projectadd.phpdagegenincs/PHPMailer_v5.1. Keine der beiden ist entfernbar, ohne den jeweiligen Aufrufer umzustellen. incs/font/gehört zu pChart, nicht zu TCPDF. Die TTFs dort werden vonstats.phpfür die Diagramme geladen; TCPDF bringt seine Schriften inincs/tcpdf/fonts/selbst mit.index.phpsetzterror_reporting(E_ERROR | E_PARSE). Warnungen und Notices sind unterdrückt, und der Code verlässt sich darauf — nicht ohne Not hochdrehen, sonst überflutet es die Ausgabe.templates_c/,ajax/templates_c/undtemp/müssen für den Webserver schreibbar sein. Sie sind git-ignoriert und nur über.gitkeepim Repo vorhanden.uploads_*/{notes,signs}enthalten personenbezogene Kundendaten (Rechnungen, Scans, Unterschriften) und sind bewusst git-ignoriert. Nie committen.- Zeilenenden werden über
.gitattributesauf LF normalisiert.
Authentifizierung
Klassischer Login plus WebAuthn/FIDO2. Server-Seite: authServer.php mit incs/WebAuthn (Bibliothek von Lukas Buchs) und incs/authHelper.php; Client-Seite js/auth.js. Schlüsselverwaltung über addKey.php und sites/userkeys.php.
Historie
Am 2026-08-28 wurden tote Pfade entfernt: smarty3/ (ungenutztes Smarty 3.1.6), incs/tcpdf_old/, incs/fpdf.php samt FPDF-Schriftmetriken und incs/font/makefont/, sites/makepdf_OLD.php, incs/users_old.php und oldindex.php. Auf keines davon verwies noch Code.
Das Repository wurde am 2026-08-28 neu initialisiert. Die mitgelieferte Historie von 2014 war irreparabel beschädigt und wurde verworfen — der erste Commit ist der Ausgangsstand, nicht der Projektbeginn.