Add CSS validation and new checkbox toggle slider styles; update validation tools documentation
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"cssList": [
|
||||
"__APPEND__",
|
||||
"client/custom/css/erstgespraech-highlight.css"
|
||||
"client/custom/css/erstgespraech-highlight.css",
|
||||
"client/custom/css/checkbox-toggle-slider.css"
|
||||
]
|
||||
}
|
||||
|
||||
292
custom/scripts/VALIDATION_TOOLS.md
Normal file
292
custom/scripts/VALIDATION_TOOLS.md
Normal 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`
|
||||
@@ -45,6 +45,7 @@ class EntityValidator:
|
||||
self.custom_path = self.base_path / "custom" / "Espo" / "Custom" / "Resources"
|
||||
self.metadata_path = self.custom_path / "metadata"
|
||||
self.i18n_path = self.custom_path / "i18n"
|
||||
self.client_custom_path = self.base_path / "client" / "custom"
|
||||
self.errors = []
|
||||
self.warnings = []
|
||||
self.entity_defs = {}
|
||||
@@ -379,9 +380,262 @@ class EntityValidator:
|
||||
print_warning(f"Konnte Dateirechte nicht prüfen: {e}")
|
||||
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:
|
||||
"""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
|
||||
is_docker_volume = '/docker/volumes/' in str(self.base_path)
|
||||
@@ -553,6 +807,18 @@ class EntityValidator:
|
||||
# 6. Dateirechte (nicht kritisch für Rebuild)
|
||||
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
|
||||
|
||||
def main():
|
||||
|
||||
Reference in New Issue
Block a user