Add CSS validation and new checkbox toggle slider styles; update validation tools documentation

This commit is contained in:
2026-01-24 00:38:56 +01:00
parent dfb0ff1fbc
commit 933df204f2
5 changed files with 869 additions and 4 deletions

View File

@@ -0,0 +1,306 @@
/**
* Custom CSS: Checkbox Toggle Slider
* Verwandelt alle Standard-Checkboxen in moderne On-Off-Slider
* Erstellt: Januar 2026
* Updated: Angepasst für EspoCRM Struktur (Label vor Input)
*/
/* ===== HAUPT-CHECKBOX STYLING ===== */
/* Standard-Checkbox verstecken und durch Slider ersetzen */
input[type="checkbox"].main-element,
input[type="checkbox"].form-checkbox,
.field[data-name] input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 44px;
height: 24px;
background-color: #ccc;
border-radius: 12px;
position: relative;
cursor: pointer;
transition: background-color 0.3s ease;
outline: none;
border: none;
vertical-align: middle;
margin: 0;
flex-shrink: 0;
}
/* Slider-Button (der runde Schalter) */
input[type="checkbox"].main-element::before,
input[type="checkbox"].form-checkbox::before,
.field[data-name] input[type="checkbox"]::before {
content: '';
position: absolute;
width: 18px;
height: 18px;
background-color: white;
border-radius: 50%;
top: 3px;
left: 3px;
transition: transform 0.3s ease, left 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
/* Checked State - Slider bewegt sich nach rechts und wird grün */
input[type="checkbox"].main-element:checked,
input[type="checkbox"].form-checkbox:checked,
.field[data-name] input[type="checkbox"]:checked {
background-color: #4CAF50;
}
input[type="checkbox"].main-element:checked::before,
input[type="checkbox"].form-checkbox:checked::before,
.field[data-name] input[type="checkbox"]:checked::before {
left: 23px;
}
/* Hover-Effekt */
input[type="checkbox"].main-element:hover,
input[type="checkbox"].form-checkbox:hover,
.field[data-name] input[type="checkbox"]:hover {
opacity: 0.9;
box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2);
}
/* Focus State für Accessibility */
input[type="checkbox"].main-element:focus,
input[type="checkbox"].form-checkbox:focus,
.field[data-name] input[type="checkbox"]:focus {
outline: 2px solid #4CAF50;
outline-offset: 2px;
}
/* Disabled State */
input[type="checkbox"].main-element:disabled,
input[type="checkbox"].form-checkbox:disabled,
.field[data-name] input[type="checkbox"]:disabled {
background-color: #e0e0e0;
cursor: not-allowed;
opacity: 0.6;
}
input[type="checkbox"].main-element:disabled::before,
input[type="checkbox"].form-checkbox:disabled::before,
.field[data-name] input[type="checkbox"]:disabled::before {
background-color: #f5f5f5;
}
/* ===== FIELD CONTAINER STYLING ===== */
/* Field Container anpassen für besseres Layout */
.field[data-name] {
display: flex;
align-items: center;
min-height: 24px;
}
/* Boolean Field spezifisch */
.field.field-boolean {
display: flex;
align-items: center;
}
/* ===== DETAIL VIEW (Readonly) ===== */
/* Detail-Mode mit custom Attribut */
.field.field-boolean[data-value="true"] input[type="checkbox"]:disabled,
.field.field-boolean.detail-mode input[type="checkbox"] {
background-color: #4CAF50;
}
.field.field-boolean[data-value="true"] input[type="checkbox"]:disabled::before,
.field.field-boolean.detail-mode input[type="checkbox"]::before {
left: 23px;
}
.field.field-boolean[data-value="false"] input[type="checkbox"]:disabled,
.field.field-boolean.detail-mode input[type="checkbox"]:not(:checked) {
background-color: #ccc;
}
/* Text "Yes"/"No" in Detail-View ausblenden falls vorhanden */
.field.field-boolean.detail-mode span.text-muted {
display: none;
}
/* ===== LIST VIEW & TABLE CELLS ===== */
/* List View Checkboxen in Tabellen */
td.cell.cell-boolean input[type="checkbox"] {
width: 44px;
height: 24px;
}
/* ===== MODAL & INLINE EDIT ===== */
/* Modal-Body Checkboxen */
.modal-body .field[data-name] input[type="checkbox"] {
width: 44px;
height: 24px;
}
/* Inline Edit Mode */
.inline-edit-mode input[type="checkbox"] {
width: 44px;
height: 24px;
}
/* ===== FILTER & SEARCH ===== */
/* Filter-Panel Checkboxen */
.search-row input[type="checkbox"] {
width: 44px;
height: 24px;
}
/* Advanced Filters */
.filter-container input[type="checkbox"] {
width: 44px;
height: 24px;
}
/* ===== PREFERENCES & SETTINGS ===== */
/* Preferences/Settings Panels */
.panel-body input[type="checkbox"].form-checkbox {
width: 44px;
height: 24px;
margin-right: 8px;
}
/* ===== MASS ACTION CHECKBOXEN AUSNAHME ===== */
/* Mass Action Checkboxen - diese NICHT als Slider darstellen */
input[type="checkbox"].record-checkbox,
input[type="checkbox"].select-all,
td.cell-checkbox input[type="checkbox"],
th.cell-checkbox input[type="checkbox"] {
appearance: checkbox !important;
-webkit-appearance: checkbox !important;
-moz-appearance: checkbox !important;
width: auto !important;
height: auto !important;
background-color: transparent !important;
border-radius: 0 !important;
}
input[type="checkbox"].record-checkbox::before,
input[type="checkbox"].select-all::before,
td.cell-checkbox input[type="checkbox"]::before,
th.cell-checkbox input[type="checkbox"]::before {
display: none !important;
content: none !important;
}
/* ===== ALTERNATIVE FARBEN (Optional) ===== */
/* Warning State */
input[type="checkbox"][data-type="warning"]:checked {
background-color: #ff9800;
}
/* Danger/Error State */
input[type="checkbox"][data-type="danger"]:checked {
background-color: #f44336;
}
/* Info State */
input[type="checkbox"][data-type="info"]:checked {
background-color: #2196F3;
}
/* ===== ANIMATIONEN ===== */
/* Slide-In Animation beim Laden */
@keyframes slideIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
input[type="checkbox"].main-element,
input[type="checkbox"].form-checkbox {
animation: slideIn 0.3s ease-out;
}
/* Smooth Toggle Animation */
input[type="checkbox"].main-element::before,
input[type="checkbox"].form-checkbox::before {
transition: left 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
}
/* ===== DARK MODE UNTERSTÜTZUNG ===== */
@media (prefers-color-scheme: dark) {
input[type="checkbox"].main-element:not(:checked),
input[type="checkbox"].form-checkbox:not(:checked) {
background-color: #555;
}
input[type="checkbox"].main-element::before,
input[type="checkbox"].form-checkbox::before {
background-color: #f0f0f0;
}
input[type="checkbox"].main-element:disabled,
input[type="checkbox"].form-checkbox:disabled {
background-color: #444;
}
}
/* ===== RESPONSIVE ANPASSUNGEN ===== */
/* Mobile Geräte - etwas größere Touch-Targets */
@media (max-width: 768px) {
input[type="checkbox"].main-element,
input[type="checkbox"].form-checkbox {
width: 48px;
height: 26px;
}
input[type="checkbox"].main-element::before,
input[type="checkbox"].form-checkbox::before {
width: 20px;
height: 20px;
}
input[type="checkbox"].main-element:checked::before,
input[type="checkbox"].form-checkbox:checked::before {
left: 25px;
}
}
/* ===== ACCESSIBILITY VERBESSERUNGEN ===== */
/* High Contrast Mode Unterstützung */
@media (prefers-contrast: high) {
input[type="checkbox"].main-element,
input[type="checkbox"].form-checkbox {
border: 2px solid currentColor;
}
input[type="checkbox"].main-element:checked,
input[type="checkbox"].form-checkbox:checked {
background-color: #2d7a2e;
}
}
/* Reduced Motion für Nutzer mit Bewegungsempfindlichkeit */
@media (prefers-reduced-motion: reduce) {
input[type="checkbox"].main-element,
input[type="checkbox"].form-checkbox,
input[type="checkbox"].main-element::before,
input[type="checkbox"].form-checkbox::before {
transition: none;
animation: none;
}
}

View File

@@ -1,6 +1,7 @@
{ {
"cssList": [ "cssList": [
"__APPEND__", "__APPEND__",
"client/custom/css/erstgespraech-highlight.css" "client/custom/css/erstgespraech-highlight.css",
"client/custom/css/checkbox-toggle-slider.css"
] ]
} }

View File

@@ -0,0 +1,292 @@
# Validierungstools für EspoCRM Custom-Entwicklung
## Installierte Tools
### 1. PHP-CLI (v8.2.29)
**Zweck:** Validierung von PHP-Syntax
**Installation:**
```bash
sudo apt install -y php-cli
```
**Verwendung im Script:**
```bash
php -l /pfad/zur/datei.php
```
**Validiert:**
- PHP-Syntax-Fehler
- Parse-Fehler
- Fehlende Klammern/Semikolons
- Ungültige Funktionsdeklarationen
---
### 2. CSSLint (v1.0.4)
**Zweck:** CSS-Validierung und Best-Practice-Checks
**Installation:**
```bash
sudo apt install -y nodejs npm
sudo npm install -g csslint
```
**Verwendung im Script:**
```bash
csslint --format=compact --quiet /pfad/zur/datei.css
```
**Validiert:**
- CSS-Syntax-Fehler
- Ungültige Properties
- Vendor-Prefix-Probleme
- Performance-Probleme
- Kompatibilitätsprobleme
**Konfiguration:**
Das Script verwendet standardmäßig alle csslint-Regeln. Für custom Rules kann eine `.csslintrc` Datei erstellt werden.
---
### 3. JSHint (v2.13.6)
**Zweck:** JavaScript-Code-Qualität und Syntax-Validierung
**Installation:**
```bash
sudo npm install -g jshint
```
**Verwendung im Script:**
```bash
jshint --config=/dev/null /pfad/zur/datei.js
```
**Validiert:**
- JavaScript-Syntax-Fehler
- Potenzielle Bugs
- Code-Style-Probleme
- Ungültige Variablen-Deklarationen
- Scope-Probleme
**Konfiguration:**
Aktuell nutzt das Script minimale Konfiguration. Für erweiterte Checks kann eine `.jshintrc` Datei erstellt werden:
```json
{
"esversion": 6,
"browser": true,
"jquery": true,
"unused": true,
"undef": true
}
```
---
## Integration im Validierungsscript
Das Script `custom/scripts/validate_and_rebuild.py` nutzt diese Tools automatisch:
### Automatische Erkennung
```python
# Prüft ob Tool verfügbar ist
try:
subprocess.run(['csslint', '--version'], capture_output=True)
use_csslint = True
except FileNotFoundError:
use_csslint = False
# Fallback auf Basis-Validierung
```
### Fallback-Mechanismus
Falls ein Tool nicht verfügbar ist, verwendet das Script eine Basis-Validierung:
- **CSS:** Klammer-Matching, Attribut-Selektoren
- **JavaScript:** Klammer-Matching (rund, eckig, geschweift)
- **PHP:** Übersprungen mit Warnung
---
## Validierungsebenen
### Kritische Fehler (Rebuild wird abgebrochen)
- JSON-Syntax-Fehler
- PHP-Syntax-Fehler
- CSS-Syntax-Fehler (schwerwiegend)
- JavaScript-Syntax-Fehler
- Fehlende Relationship-Definitionen
### Warnungen (Rebuild wird fortgesetzt)
- CSS-Best-Practice-Verletzungen
- Fehlende i18n-Übersetzungen
- Code-Style-Probleme
- Performance-Hinweise
---
## Manuelle Verwendung der Tools
### CSS prüfen
```bash
# Einzelne Datei
csslint client/custom/css/my-styles.css
# Alle CSS-Dateien
find client/custom/css -name "*.css" -exec csslint {} \;
# Mit spezifischen Regeln
csslint --errors=errors,duplicate-properties client/custom/css/
```
### JavaScript prüfen
```bash
# Einzelne Datei
jshint client/custom/src/views/my-view.js
# Alle JS-Dateien
find client/custom/src -name "*.js" -exec jshint {} \;
# Mit Konfiguration
jshint --config .jshintrc client/custom/src/
```
### PHP prüfen
```bash
# Einzelne Datei
php -l custom/Espo/Custom/Classes/MyClass.php
# Alle PHP-Dateien
find custom/Espo -name "*.php" -exec php -l {} \; | grep -v "No syntax errors"
```
---
## Zusätzliche empfohlene Tools (optional)
### 1. ESLint (moderne Alternative zu JSHint)
```bash
sudo npm install -g eslint
eslint --init # Erstellt .eslintrc Konfiguration
```
### 2. Stylelint (moderne Alternative zu CSSLint)
```bash
sudo npm install -g stylelint stylelint-config-standard
# Benötigt .stylelintrc Konfiguration
```
### 3. PHPStan (statische PHP-Analyse)
```bash
composer require --dev phpstan/phpstan
vendor/bin/phpstan analyse custom/Espo
```
### 4. PHP_CodeSniffer (PHP Code-Style)
```bash
sudo apt install -y php-codesniffer
phpcs --standard=PSR12 custom/Espo/
```
---
## Troubleshooting
### Tool nicht gefunden
```bash
# Prüfe Installation
which csslint
which jshint
which php
# Installiere fehlende Tools
sudo apt update
sudo apt install -y nodejs npm php-cli
sudo npm install -g csslint jshint
```
### Validierung zu streng
Bearbeite das Script und passe die Tool-Parameter an:
```python
# Weniger strikte CSS-Validierung
result = subprocess.run(
['csslint', '--format=compact', '--errors=errors', str(css_file)],
...
)
```
### Performance-Probleme
Für große Projekte mit vielen Dateien:
```python
# Parallele Validierung mit ThreadPoolExecutor
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=4) as executor:
results = executor.map(validate_css_file, css_files)
```
---
## Best Practices
1. **Vor jedem Commit:** Führe Validierung aus
```bash
python3 custom/scripts/validate_and_rebuild.py --dry-run
```
2. **Automatisierung:** Integriere in Git pre-commit hook
```bash
# .git/hooks/pre-commit
#!/bin/bash
python3 custom/scripts/validate_and_rebuild.py --dry-run
if [ $? -ne 0 ]; then
echo "Validierung fehlgeschlagen. Commit abgebrochen."
exit 1
fi
```
3. **CI/CD Integration:** Nutze in Build-Pipeline
```yaml
# .gitlab-ci.yml / .github/workflows/
validate:
script:
- python3 custom/scripts/validate_and_rebuild.py --dry-run
```
4. **IDE Integration:** Konfiguriere Editor für Live-Feedback
- VSCode: ESLint/Stylelint Extensions
- PHPStorm: Built-in PHP/JS/CSS Validierung
---
## Maintenance
### Updates der Tools
```bash
# System-Packages
sudo apt update && sudo apt upgrade
# NPM-Packages
sudo npm update -g csslint jshint
# Prüfe Versionen
php --version
csslint --version
jshint --version
```
### Entfernung
```bash
# NPM-Tools
sudo npm uninstall -g csslint jshint
# APT-Packages
sudo apt remove php-cli nodejs npm
sudo apt autoremove
```
---
**Letzte Aktualisierung:** Januar 2026
**Maintainer:** Custom Scripts Team
**Dokumentation:** `custom/scripts/VALIDATION_TOOLS.md`

View File

@@ -45,6 +45,7 @@ class EntityValidator:
self.custom_path = self.base_path / "custom" / "Espo" / "Custom" / "Resources" self.custom_path = self.base_path / "custom" / "Espo" / "Custom" / "Resources"
self.metadata_path = self.custom_path / "metadata" self.metadata_path = self.custom_path / "metadata"
self.i18n_path = self.custom_path / "i18n" self.i18n_path = self.custom_path / "i18n"
self.client_custom_path = self.base_path / "client" / "custom"
self.errors = [] self.errors = []
self.warnings = [] self.warnings = []
self.entity_defs = {} self.entity_defs = {}
@@ -379,9 +380,262 @@ class EntityValidator:
print_warning(f"Konnte Dateirechte nicht prüfen: {e}") print_warning(f"Konnte Dateirechte nicht prüfen: {e}")
return True return True
def validate_css_files(self) -> bool:
"""Validiere CSS-Dateien mit csslint."""
print_header("7. CSS-VALIDIERUNG")
css_files = []
if self.client_custom_path.exists():
css_files = list((self.client_custom_path / "css").rglob("*.css")) if (self.client_custom_path / "css").exists() else []
if not css_files:
print_info("Keine CSS-Dateien gefunden")
return True
# Prüfe ob csslint verfügbar ist
try:
subprocess.run(['csslint', '--version'], capture_output=True, timeout=5)
use_csslint = True
except (FileNotFoundError, subprocess.TimeoutExpired):
use_csslint = False
print_warning("csslint nicht gefunden, verwende Basis-Validierung")
invalid_files = []
for css_file in css_files:
if use_csslint:
# Verwende csslint für professionelle Validierung
try:
result = subprocess.run(
['csslint', '--format=compact', '--quiet', str(css_file)],
capture_output=True,
text=True,
timeout=10
)
if result.returncode != 0 and result.stdout.strip():
# Parse csslint Ausgabe
errors = []
for line in result.stdout.strip().split('\n'):
if 'Error' in line or 'Warning' in line:
# Extrahiere nur die Fehlermeldung ohne Pfad
parts = line.split(': ', 2)
if len(parts) >= 3:
errors.append(parts[2])
else:
errors.append(line)
if errors:
# Nur echte Fehler, keine Warnungen als kritisch behandeln
if any('Error' in err for err in errors):
self.errors.append(f"CSS-Fehler in {css_file.relative_to(self.base_path)}")
invalid_files.append((str(css_file.relative_to(self.base_path)), errors))
else:
# Nur Warnungen
for err in errors:
self.warnings.append(f"{css_file.relative_to(self.base_path)}: {err}")
except subprocess.TimeoutExpired:
self.warnings.append(f"CSS-Validierung timeout für {css_file.relative_to(self.base_path)}")
except Exception as e:
self.warnings.append(f"Konnte {css_file.relative_to(self.base_path)} nicht validieren: {e}")
else:
# Fallback: Basis-Validierung
try:
with open(css_file, 'r', encoding='utf-8') as f:
content = f.read()
errors = []
# Geschlossene Klammern
open_braces = content.count('{')
close_braces = content.count('}')
if open_braces != close_braces:
errors.append(f"Ungleiche Klammern: {open_braces} {{ vs {close_braces} }}")
if errors:
self.errors.append(f"CSS-Fehler in {css_file.relative_to(self.base_path)}")
invalid_files.append((str(css_file.relative_to(self.base_path)), errors))
except Exception as e:
self.errors.append(f"Konnte {css_file.relative_to(self.base_path)} nicht lesen: {e}")
invalid_files.append((str(css_file.relative_to(self.base_path)), [str(e)]))
if invalid_files:
print_error(f"{len(invalid_files)} CSS-Datei(en) mit Fehlern:")
for filepath, errors in invalid_files:
print(f" {Colors.RED}{Colors.END} {filepath}")
for err in errors[:5]: # Maximal 5 Fehler pro Datei anzeigen
print(f" {Colors.RED}{Colors.END} {err}")
if len(errors) > 5:
print(f" {Colors.RED}...{Colors.END} und {len(errors) - 5} weitere Fehler")
return False
else:
print_success(f"Alle {len(css_files)} CSS-Dateien sind syntaktisch korrekt")
return True
def validate_js_files(self) -> bool:
"""Validiere JavaScript-Dateien mit jshint."""
print_header("8. JAVASCRIPT-VALIDIERUNG")
js_files = []
if self.client_custom_path.exists():
src_path = self.client_custom_path / "src"
js_files = list(src_path.rglob("*.js")) if src_path.exists() else []
if not js_files:
print_info("Keine JavaScript-Dateien gefunden")
return True
# Prüfe ob jshint verfügbar ist
try:
subprocess.run(['jshint', '--version'], capture_output=True, timeout=5)
use_jshint = True
except (FileNotFoundError, subprocess.TimeoutExpired):
use_jshint = False
print_warning("jshint nicht gefunden, verwende Basis-Validierung")
invalid_files = []
for js_file in js_files:
if use_jshint:
# Verwende jshint für professionelle Validierung
try:
result = subprocess.run(
['jshint', '--config=/dev/null', str(js_file)],
capture_output=True,
text=True,
timeout=10
)
if result.returncode != 0 and result.stdout.strip():
errors = []
for line in result.stdout.strip().split('\n'):
if line and not line.startswith('Lint'):
# Parse jshint Ausgabe
errors.append(line)
if errors:
self.errors.append(f"JavaScript-Fehler in {js_file.relative_to(self.base_path)}")
invalid_files.append((str(js_file.relative_to(self.base_path)), errors))
except subprocess.TimeoutExpired:
self.warnings.append(f"JavaScript-Validierung timeout für {js_file.relative_to(self.base_path)}")
except Exception as e:
self.warnings.append(f"Konnte {js_file.relative_to(self.base_path)} nicht validieren: {e}")
else:
# Fallback: Basis-Validierung
try:
with open(js_file, 'r', encoding='utf-8') as f:
content = f.read()
errors = []
# Geschlossene Klammern
open_paren = content.count('(')
close_paren = content.count(')')
if open_paren != close_paren:
errors.append(f"Ungleiche runde Klammern: {open_paren} ( vs {close_paren} )")
open_braces = content.count('{')
close_braces = content.count('}')
if open_braces != close_braces:
errors.append(f"Ungleiche geschweifte Klammern: {open_braces} {{ vs {close_braces} }}")
open_brackets = content.count('[')
close_brackets = content.count(']')
if open_brackets != close_brackets:
errors.append(f"Ungleiche eckige Klammern: {open_brackets} [ vs {close_brackets} ]")
if errors:
self.errors.append(f"JavaScript-Fehler in {js_file.relative_to(self.base_path)}")
invalid_files.append((str(js_file.relative_to(self.base_path)), errors))
except Exception as e:
self.errors.append(f"Konnte {js_file.relative_to(self.base_path)} nicht lesen: {e}")
invalid_files.append((str(js_file.relative_to(self.base_path)), [str(e)]))
if invalid_files:
print_error(f"{len(invalid_files)} JavaScript-Datei(en) mit Fehlern:")
for filepath, errors in invalid_files:
print(f" {Colors.RED}{Colors.END} {filepath}")
for err in errors[:5]: # Maximal 5 Fehler pro Datei
print(f" {Colors.RED}{Colors.END} {err}")
if len(errors) > 5:
print(f" {Colors.RED}...{Colors.END} und {len(errors) - 5} weitere Fehler")
return False
else:
print_success(f"Alle {len(js_files)} JavaScript-Dateien sind syntaktisch korrekt")
return True
def validate_php_files(self) -> bool:
"""Validiere PHP-Dateien mit php -l (Lint)."""
print_header("9. PHP-VALIDIERUNG")
php_files = []
custom_espo_path = self.base_path / "custom" / "Espo"
if custom_espo_path.exists():
php_files = list(custom_espo_path.rglob("*.php"))
if not php_files:
print_info("Keine PHP-Dateien gefunden")
return True
# Prüfe ob php verfügbar ist
try:
subprocess.run(['php', '--version'], capture_output=True, timeout=5)
except (FileNotFoundError, subprocess.TimeoutExpired):
print_warning("PHP-CLI nicht gefunden, überspringe PHP-Validierung")
return True
invalid_files = []
for php_file in php_files:
try:
# Verwende php -l für Syntax-Check
result = subprocess.run(
['php', '-l', str(php_file)],
capture_output=True,
text=True,
timeout=5
)
if result.returncode != 0:
error_lines = []
output = result.stderr.strip() or result.stdout.strip()
for line in output.split('\n'):
# Filtere die relevanten Fehlerzeilen
if line and not line.startswith('No syntax errors'):
# Entferne Datei-Pfad aus Fehlermeldung für bessere Lesbarkeit
clean_line = re.sub(r'^.*?in\s+.*?on\s+', '', line)
if clean_line != line: # Wenn Ersetzung stattfand
error_lines.append(clean_line)
else:
error_lines.append(line)
if error_lines:
self.errors.append(f"PHP-Syntax-Fehler in {php_file.relative_to(self.base_path)}")
invalid_files.append((str(php_file.relative_to(self.base_path)), error_lines))
except subprocess.TimeoutExpired:
self.warnings.append(f"PHP-Validierung timeout für {php_file.relative_to(self.base_path)}")
except Exception as e:
self.warnings.append(f"Konnte {php_file.relative_to(self.base_path)} nicht validieren: {e}")
if invalid_files:
print_error(f"{len(invalid_files)} PHP-Datei(en) mit Syntax-Fehlern:")
for filepath, errors in invalid_files:
print(f" {Colors.RED}{Colors.END} {filepath}")
for err in errors[:3]: # Maximal 3 Fehler pro Datei
print(f" {Colors.RED}{Colors.END} {err}")
if len(errors) > 3:
print(f" {Colors.RED}...{Colors.END} und {len(errors) - 3} weitere Fehler")
return False
else:
print_success(f"Alle {len(php_files)} PHP-Dateien sind syntaktisch korrekt")
return True
def run_rebuild(self) -> bool: def run_rebuild(self) -> bool:
"""Führe den EspoCRM Rebuild aus.""" """Führe den EspoCRM Rebuild aus."""
print_header("7. ESPOCRM REBUILD") print_header("10. ESPOCRM REBUILD")
# Prüfe ob wir in einem Docker-Volume sind # Prüfe ob wir in einem Docker-Volume sind
is_docker_volume = '/docker/volumes/' in str(self.base_path) is_docker_volume = '/docker/volumes/' in str(self.base_path)
@@ -553,6 +807,18 @@ class EntityValidator:
# 6. Dateirechte (nicht kritisch für Rebuild) # 6. Dateirechte (nicht kritisch für Rebuild)
self.check_file_permissions() self.check_file_permissions()
# 7. CSS-Validierung (kritisch)
if not self.validate_css_files():
all_valid = False
# 8. JavaScript-Validierung (kritisch)
if not self.validate_js_files():
all_valid = False
# 9. PHP-Validierung (kritisch)
if not self.validate_php_files():
all_valid = False
return all_valid return all_valid
def main(): def main():

View File

@@ -359,8 +359,8 @@ return [
0 => 'youtube.com', 0 => 'youtube.com',
1 => 'google.com' 1 => 'google.com'
], ],
'cacheTimestamp' => 1769210138, 'cacheTimestamp' => 1769211423,
'microtime' => 1769210138.514983, 'microtime' => 1769211423.866265,
'siteUrl' => 'https://crm.bitbylaw.com', 'siteUrl' => 'https://crm.bitbylaw.com',
'fullTextSearchMinLength' => 4, 'fullTextSearchMinLength' => 4,
'appTimestamp' => 1768843902, 'appTimestamp' => 1768843902,