Compare commits
76
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d453b93a0 | ||
|
|
85de71f9b4 | ||
|
|
d88db54635 | ||
|
|
24cc12374f | ||
|
|
9ec0465401 | ||
|
|
133b1d57ed | ||
|
|
1ad59a9881 | ||
|
|
c540d06939 | ||
|
|
52c11cc1b9 | ||
|
|
37fa819e97 | ||
|
|
6922bfa99b | ||
|
|
ce9fe75e9c | ||
|
|
8bccdddebe | ||
|
|
1228eccf52 | ||
|
|
973aa67153 | ||
|
|
a84b13a5c2 | ||
|
|
5ebed74e12 | ||
|
|
b80e44fd32 | ||
|
|
5d027879ba | ||
|
|
854e6d03cb | ||
|
|
66eb55d264 | ||
|
|
26f5519888 | ||
|
|
405b9c27e7 | ||
|
|
6ad068c940 | ||
|
|
ecea3fe741 | ||
|
|
e570dcce25 | ||
|
|
951253c559 | ||
|
|
8f84260186 | ||
|
|
448bffd2f5 | ||
|
|
db392d915d | ||
|
|
584f168074 | ||
|
|
ce0d16c1ed | ||
|
|
b1416aa933 | ||
|
|
c8f4f47439 | ||
|
|
86c20a63bf | ||
|
|
1fba004b9b | ||
|
|
9622e35ca3 | ||
|
|
2748349060 | ||
|
|
ebbb6551f7 | ||
|
|
992ac5eb6a | ||
|
|
d37937f4d8 | ||
|
|
523400ac0e | ||
|
|
2064546b19 | ||
|
|
d71e2c1e2d | ||
|
|
62dadd175c | ||
|
|
3c9eb7091f | ||
|
|
7abcac5f04 | ||
|
|
7d03579d31 | ||
|
|
742c5d5b56 | ||
|
|
0952d6de13 | ||
|
|
5a9a08421f | ||
|
|
ffcc8821ed | ||
|
|
72f188e239 | ||
|
|
6ec159f75c | ||
|
|
d6e70a08eb | ||
|
|
52446cc019 | ||
|
|
10472843a7 | ||
|
|
771f9e7bf6 | ||
|
|
a066c59576 | ||
|
|
388f9b8f0d | ||
|
|
b08857a4cd | ||
|
|
6d68fbc25d | ||
|
|
dceb02a3de | ||
|
|
cd325270f0 | ||
|
|
efa9584baf | ||
|
|
ac202be4a7 | ||
|
|
24e11e14a3 | ||
|
|
5305fcabb6 | ||
|
|
3f7c840c39 | ||
|
|
378c462b1c | ||
|
|
5f1aa1c6fc | ||
|
|
5ac4fa4ac6 | ||
|
|
3eb451069b | ||
|
|
caf62ea701 | ||
|
|
82fe6e85d8 | ||
|
|
40c26e625b |
No files matched your search
@@ -11,12 +11,6 @@
|
||||
"activityBar.foreground": "#000000",
|
||||
"breadcrumb.foreground": "#FFFFFF",
|
||||
},
|
||||
"files.exclude": {
|
||||
"*.git*": true,
|
||||
"WEBROOT/config_backups": true,
|
||||
"WEBROOT/static": true,
|
||||
"WEBROOT/templates": true,
|
||||
},
|
||||
"editor.renderWhitespace": "all",
|
||||
"editor.smoothScrolling": true,
|
||||
"editor.fontSize": 14,
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
# 🏗️ Backend MVC Framework - Das Handbuch
|
||||
|
||||
Dieses Framework ist eine performante, leichtgewichtige und modulare PHP-Architektur, die speziell für die Entwicklung von professionellen Backends und Dashboards entwickelt wurde. Es implementiert das **Model-View-Controller (MVC)** Pattern und trennt strikt zwischen Datenlogik, Geschäftslogik und UI-Design.
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ 1. Architektur & Verzeichnisstruktur
|
||||
|
||||
Das Herzstück des Frameworks liegt in `WEBROOT/src/MVC/`. Die Struktur ist darauf ausgelegt, dass das Projekt beliebig wachsen kann, ohne unübersichtlich zu werden.
|
||||
|
||||
### 📂 Ordner-Struktur im Detail
|
||||
|
||||
* **`WEBROOT/index.php`**: Der zentrale Einstiegspunkt (Front Controller). Jede Anfrage läuft hier auf. Hier werden Routen registriert und das Layout zusammengesetzt.
|
||||
* **`src/MVC/Core/`**:
|
||||
* `Controller.php`: Basisklasse für alle Controller. Bietet Methoden für Rendering und JSON-Output.
|
||||
* `Model.php`: Basisklasse für Datenbankzugriffe. Implementiert sichere CRUD-Methoden via Prepared Statements.
|
||||
* `View.php`: Die Engine, die Daten in HTML-Templates extrahiert.
|
||||
* `Router.php`: Verwaltet Routen und schaltet Middlewares (Auth/Admin) vor.
|
||||
* `Layout/Layout.php`: Das Block-System für modulares UI-Design.
|
||||
* **`src/MVC/Controller/`**: Hier landen deine PHP-Klassen für die Logik. Beispiel: `UserController.php`, `LogController.php`.
|
||||
* **`src/MVC/Model/`**: Jede Klasse entspricht einer Datenbanktabelle. Hier wird definiert, *wie* Daten geholt oder gespeichert werden.
|
||||
* **`src/MVC/View/`**: Reine HTML-Dateien mit minimalem PHP-Code für die Anzeige.
|
||||
* **`src/MVC/Layout/Templates/`**: Enthält das Grundgerüst (`main.php`), in das die Views eingebettet werden.
|
||||
|
||||
---
|
||||
|
||||
## 🚦 2. Der Request-Lifecycle (Wie eine Seite entsteht)
|
||||
|
||||
1. Ein Benutzer ruft `?service=benutzer` auf.
|
||||
2. Die `index.php` lädt den **Autoloader** und initialisiert den **Router**.
|
||||
3. Der **Router** prüft, ob eine Middleware (z.B. `auth`) für diesen Service konfiguriert ist.
|
||||
4. Wenn der Zugriff erlaubt ist, erstellt der Router eine Instanz des jeweiligen **Controllers** und ruft die Zielmethode auf.
|
||||
5. Der **Controller** nutzt ein **Model**, um Daten aus der Datenbank zu holen.
|
||||
6. Der Controller übergibt diese Daten an eine **View** (`$this->render()`).
|
||||
7. Die **index.php** nimmt das HTML der View entgegen, packt es in den `content`-Block des **Layouts** und gibt die fertige Seite aus.
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ 3. Arbeiten mit dem Model-Layer (Datenbank)
|
||||
|
||||
Vermeide SQL direkt im Controller! Nutze immer Models für eine saubere Abstraktion.
|
||||
|
||||
### Beispiel: Ein eigenes Model erstellen
|
||||
```php
|
||||
namespace MVC\Model;
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Product extends Model {
|
||||
protected static $table = "Produkte"; // Tabellenname in der DB
|
||||
|
||||
public static function getExpensiveProducts() {
|
||||
return self::where("PREIS > ?", 100); // Sicher gegen SQL-Injection!
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Verfügbare Methoden der Basisklasse:
|
||||
* `all()`: Lädt alles aus der Tabelle.
|
||||
* `find($id)`: Findet einen Datensatz per ID.
|
||||
* `where($sql, ...$params)`: Filtert mit Prepared Statements.
|
||||
* `insert($array)`: Speichert neue Daten (z.B. `['TITEL' => 'Test']`).
|
||||
* `update($array, $where, ...$params)`: Aktualisiert bestehende Daten.
|
||||
* `delete($where, ...$params)`: Löscht Datensätze sicher.
|
||||
|
||||
---
|
||||
|
||||
## 🎮 4. Controller & Formularverarbeitung
|
||||
|
||||
Controller steuern den Ablauf. Sie entscheiden, was bei `GET` und was bei `POST` passiert.
|
||||
|
||||
### Beispiel: Daten verarbeiten
|
||||
```php
|
||||
public function save() {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Daten via Model speichern
|
||||
User::update(['STATUS' => 'aktiv'], "ID = ?", $_POST['id']);
|
||||
|
||||
// WICHTIG: Danach immer Redirect (Post-Redirect-Get Pattern)
|
||||
header("Location: ?service=user_list");
|
||||
exit;
|
||||
}
|
||||
return $this->render('edit_view');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧱 5. Das modulare Block-Layout
|
||||
|
||||
Das Layout-System ermöglicht es, verschiedene Teile der Seite (Header, Sidebar, Content) unabhängig voneinander zu befüllen.
|
||||
|
||||
### Blöcke in `index.php` befüllen:
|
||||
```php
|
||||
$layout = new \MVC\Layout\Layout('main');
|
||||
$layout->setBlock('title', 'Admin Dashboard');
|
||||
$layout->setBlock('sidebar', $sidebarHtml);
|
||||
$layout->setBlock('content', $controllerOutput);
|
||||
```
|
||||
|
||||
### Blöcke im Template (`main.php`) ausgeben:
|
||||
```html
|
||||
<aside class="sidebar">
|
||||
<?= $this->getBlock('sidebar') ?>
|
||||
</aside>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 6. CLI-Werkzeuge für Entwickler
|
||||
|
||||
Das Framework kommt mit automatisierten Scripten, um lästige Routinearbeiten zu vermeiden.
|
||||
|
||||
### Projekt initialisieren
|
||||
Erstellt ein komplettes Skelett inkl. Dev-Server und API.
|
||||
```bash
|
||||
php init_mvc.php MeinProjekt
|
||||
```
|
||||
|
||||
### Modul (Controller + View) erstellen
|
||||
Erstellt zwei Dateien und gibt dir den Code für den Router aus.
|
||||
```bash
|
||||
php create_module.php kunden_verwaltung
|
||||
```
|
||||
|
||||
### Model erstellen
|
||||
Erstellt eine Model-Klasse mit Tabellenzuweisung und dokumentierten Spalten.
|
||||
```bash
|
||||
php create_model.php Kunden ID,NAME,EMAIL,TELEFON
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 7. Sicherheit & Best Practices
|
||||
|
||||
1. **XSS-Schutz**: Gib niemals Variablen direkt in der View aus. Nutze immer `<?= htmlspecialchars($var) ?>`.
|
||||
2. **SQL-Injection**: Nutze niemals Variablen direkt in einem SQL-String. Nutze die Methoden `where()`, `update()` etc. des Models. Sie verwenden intern Prepared Statements.
|
||||
3. **Namespaces**: Alle Klassen im `src/MVC` Ordner nutzen Namespaces. Achte beim Aufruf globaler Klassen (außerhalb des MVC-Ordners) auf den führenden Backslash, z.B. `\db_connect()`.
|
||||
4. **Error Handling**: Fehler werden zentral in der `index.php` gefangen. Admins erhalten detaillierte Informationen, User sehen eine sichere Fehlermeldung.
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 8. AJAX & API Entwicklung
|
||||
|
||||
Um Daten ohne Seiten-Reload zu laden, bietet der Controller die `json()` Methode:
|
||||
|
||||
```php
|
||||
public function getStatus() {
|
||||
$data = ['online' => true, 'time' => date('H:i')];
|
||||
return $this->json($data); // Setzt Header und beendet Script
|
||||
}
|
||||
```
|
||||
|
||||
In deinem JavaScript kannst du dies dann einfach per `fetch()` abrufen.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 9. Installation & Start
|
||||
|
||||
1. Stelle sicher, dass PHP 8.x installiert ist.
|
||||
2. Passe die `src/config/env.ini` mit deinen Datenbankdaten an.
|
||||
3. Starte den internen Server: `php server.php`.
|
||||
4. Öffne `http://localhost:8000` im Browser.
|
||||
|
||||
---
|
||||
|
||||
*Dieses Framework wurde für maximale Produktivität entwickelt. Bei Fragen schau in die integrierte Dokumentation im Backend.*
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
// loginTest.php
|
||||
|
||||
$loginUrl = "https://backend.blackpi.ipv64.net/login.php"; // URL zu deinem Login-Script
|
||||
$loginUrl = "https://backend.mrhossa.de/login.php"; // URL zu deinem Login-Script
|
||||
|
||||
// Testfälle
|
||||
$tests = [
|
||||
@@ -51,4 +51,4 @@ foreach ($tests as $test) {
|
||||
} else {
|
||||
echo "[FAIL] {$test['description']}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
// reset_einkaufsliste.php
|
||||
// Aufruf z.B. per Cron: php /pfad/zu/reset_einkaufsliste.php
|
||||
|
||||
include_once(__DIR__ . '/../controller/DatabaseController.php');
|
||||
|
||||
$result = DatabaseUpdate(
|
||||
'Einkaufsliste',
|
||||
'`STATUS` = ?',
|
||||
'`TYP` = ? AND `STATUS` = ?',
|
||||
'aktiv', 'vorlage', 'erledigt'
|
||||
);
|
||||
|
||||
echo $result
|
||||
? "Vorlagen-Artikel wurden zurückgesetzt.\n"
|
||||
: "Fehler beim Zurücksetzen.\n";
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
include_once(__DIR__ . '/../../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../../controller/DataController.php');
|
||||
|
||||
class changeTimetable{
|
||||
/**
|
||||
* get Teachers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTeachers() : array {
|
||||
$teachers = [];
|
||||
$teachers = DatabaseShow(
|
||||
'Lehrer',
|
||||
'NAME'
|
||||
);
|
||||
|
||||
return $teachers;
|
||||
}
|
||||
|
||||
/**
|
||||
* get Rooms
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRooms() : array {
|
||||
$rooms = [];
|
||||
$rooms = DatabaseShow(
|
||||
'Raum',
|
||||
'RAUM_NR'
|
||||
);
|
||||
|
||||
return $rooms;
|
||||
}
|
||||
|
||||
/**
|
||||
* get Lessons
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLessons() : array {
|
||||
$lessons = [];
|
||||
$lessons = DatabaseShow(
|
||||
'Fach',
|
||||
'FACH'
|
||||
);
|
||||
|
||||
return $lessons;
|
||||
}
|
||||
|
||||
/**
|
||||
* add a new Teacher to the Table
|
||||
*
|
||||
* @param array $teachers
|
||||
* @return boolean
|
||||
*/
|
||||
public function addTeachers(array $teachers) : bool {
|
||||
try {
|
||||
foreach ($teachers as $teacher) {
|
||||
DatabaseInsert(
|
||||
'Lehrer',
|
||||
'NAME',
|
||||
$teacher
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception $error) {
|
||||
echo $error;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add a new Room Number to the Tabke
|
||||
*
|
||||
* @param array $roomnrs
|
||||
* @return bool
|
||||
*/
|
||||
public function addRooms(array $roomnrs) : bool {
|
||||
try {
|
||||
foreach ($roomnrs as $roomnr) {
|
||||
DatabaseInsert(
|
||||
'Raum',
|
||||
'RAUM_NR',
|
||||
$roomnr,
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception $error) {
|
||||
echo $error;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function addLessons(array $lessons) : bool {
|
||||
try {
|
||||
foreach ($lessons as $lesson) {
|
||||
DatabaseInsert(
|
||||
'Fach',
|
||||
'FACH',
|
||||
$lesson
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception $error) {
|
||||
echo $error;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function setInitial(string $Teacher, string $Initial) : bool {
|
||||
try {
|
||||
$ID = DatabaseShow(
|
||||
'Lehrer',
|
||||
'ID',
|
||||
'NAME = ?',
|
||||
$Teacher,
|
||||
);
|
||||
|
||||
$ID = $ID[0]['ID'];
|
||||
|
||||
DatabaseUpdate(
|
||||
'Lehrer',
|
||||
'INITIALS = ?',
|
||||
'ID = ?',
|
||||
$Initial,
|
||||
$ID
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (Exception $error) {
|
||||
echo $error;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$TimeTable = new changeTimetable();
|
||||
|
||||
#$TimeTable->addTeachers([
|
||||
# '',
|
||||
#]);
|
||||
|
||||
#$TimeTable->addRooms([
|
||||
# '',
|
||||
#]);
|
||||
|
||||
#$TimeTable->addLessons([
|
||||
# '',
|
||||
#]);
|
||||
|
||||
#(new changeTimetable()->addLessons(['', '']));
|
||||
|
||||
#(new changeTimetable()->setInitial('', ''));
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
include_once(__DIR__ . "/../controller/DatabaseController.php");
|
||||
include_once(__DIR__ . "/../modules/Mail.php");
|
||||
|
||||
date_default_timezone_set("Europe/Berlin");
|
||||
|
||||
$jetzt = new DateTime();
|
||||
$heute = $jetzt->format("Y-m-d");
|
||||
|
||||
// Alle heutigen Termine laden
|
||||
$termine = DatabaseShow(
|
||||
"Termine",
|
||||
"*",
|
||||
"DATUM = ? AND ERINNERUNG_AM IS NULL",
|
||||
$heute
|
||||
);
|
||||
|
||||
foreach ($termine as $termin) {
|
||||
|
||||
$terminZeit = new DateTime($termin["DATUM"] . " " . $termin["ZEIT"]);
|
||||
$sekundenBisTermin = $terminZeit->getTimestamp() - $jetzt->getTimestamp();
|
||||
|
||||
// Nur Termine innerhalb der nächsten 30 Minuten
|
||||
if ($sekundenBisTermin > 0 && $sekundenBisTermin <= 1800) {
|
||||
|
||||
// Benutzer laden
|
||||
$benutzer = DatabaseShow(
|
||||
"Benutzer",
|
||||
"*",
|
||||
"NAME = ?",
|
||||
$termin["ERSTELLT_VON"]
|
||||
);
|
||||
|
||||
if (empty($benutzer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$benutzer = $benutzer[0];
|
||||
|
||||
if (empty($benutzer["EMAIL"])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$subject = "Terminerinnerung";
|
||||
|
||||
$message = "Hallo {$benutzer["NAME"]},\n\n";
|
||||
$message .= "Du hast in weniger als 30 Minuten einen Termin.\n\n";
|
||||
$message .= "Termin: {$termin["NAME"]}\n";
|
||||
$message .= "Datum: {$termin["DATUM"]}\n";
|
||||
$message .= "Uhrzeit: {$termin["ZEIT"]}\n";
|
||||
|
||||
if (!empty($termin["NOTIZ"])) {
|
||||
$notizClean = mb_convert_encoding($termin["NOTIZ"], "UTF-8", "auto");
|
||||
$message .= "Notiz: {$notizClean}\n";
|
||||
}
|
||||
|
||||
sendMail(
|
||||
$subject,
|
||||
$message,
|
||||
$_SERVER['SCRIPT_NAME'] ?? 'CLI/Unbekannt',
|
||||
true,
|
||||
$benutzer["EMAIL"],
|
||||
);
|
||||
|
||||
print_r("{$benutzer["EMAIL"]}\n{$message}");
|
||||
|
||||
DatabaseUpdate(
|
||||
"Termine",
|
||||
"ERINNERUNG_AM = ?",
|
||||
"ID = ?",
|
||||
date('d-m-Y H:i:s'),
|
||||
$termin["ID"]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
include_once( __DIR__ . '/../src/utils/databaseMethods.php');
|
||||
include_once( __DIR__ . '/../src/classes/getData.php');
|
||||
include_once( __DIR__ . '/../src/database/databaseConnect.php');
|
||||
include_once(__DIR__ . "/../controller/DataController.php");
|
||||
include_once(__DIR__ . "/../controller/DatabaseConnect.php");
|
||||
include_once(__DIR__ . "/../controller/DatabaseController.php");
|
||||
|
||||
$data = new getSpecificData();
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/../../controller/DatabaseController.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
$deniedTables = [];
|
||||
|
||||
$validOperations = ['=', '!=', '<', '>', '<=', '>=', 'LIKE'];
|
||||
|
||||
function isValidEntry(string $name): bool {
|
||||
return (bool) preg_match('/^[a-zA-Z0-9_]+$/', $name);
|
||||
}
|
||||
|
||||
function PermittedSchemas(array $deniedTables): array {
|
||||
static $cache = null;
|
||||
if ($cache !== null) {
|
||||
return $cache;
|
||||
}
|
||||
|
||||
$alleSchemas = DatabaseShowAllSchemas(); // 1 Query statt N+1
|
||||
$schema = [];
|
||||
|
||||
foreach ($alleSchemas as $table => $columns) {
|
||||
if (!isValidEntry($table)) {
|
||||
continue;
|
||||
}
|
||||
if (in_array($table, $deniedTables, true)) {
|
||||
continue;
|
||||
}
|
||||
$schema[$table] = $columns;
|
||||
}
|
||||
|
||||
$cache = $schema;
|
||||
return $schema;
|
||||
}
|
||||
|
||||
$erlaubteSchema = PermittedSchemas($deniedTables);
|
||||
|
||||
function isValidTable(string $table, array $schema): void {
|
||||
if (!isValidEntry($table) || !isset($schema[$table])) {
|
||||
throw new InvalidArgumentException("Tabelle nicht erlaubt: $table");
|
||||
}
|
||||
}
|
||||
|
||||
function isValidColumn(string $table, array $columns, array $schema): void {
|
||||
foreach ($columns as $col) {
|
||||
if (!isValidEntry($col) || !in_array($col, $schema[$table], true)) {
|
||||
throw new InvalidArgumentException("Spalte nicht erlaubt: $col");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildWhere(array $conditions, string $table, array $schema, array $validOperations): array {
|
||||
$parts = [];
|
||||
$values = [];
|
||||
|
||||
foreach ($conditions as $cond) {
|
||||
$col = $cond['column'] ?? '';
|
||||
$op = $cond['operator'] ?? '=';
|
||||
$val = $cond['value'] ?? null;
|
||||
|
||||
isValidColumn($table, [$col], $schema);
|
||||
|
||||
if (!in_array($op, $validOperations, true)) {
|
||||
throw new InvalidArgumentException("Ungültiger Operator: $op");
|
||||
}
|
||||
|
||||
$parts[] = "`$col` $op ?";
|
||||
$values[] = $val;
|
||||
}
|
||||
|
||||
return [implode(' AND ', $parts), $values];
|
||||
}
|
||||
|
||||
// Request einlesen
|
||||
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $daten['action'] ?? '';
|
||||
$table = $daten['table'] ?? '';
|
||||
|
||||
try {
|
||||
isValidTable($table, $erlaubteSchema);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'insert': {
|
||||
$columns = $daten['columns'] ?? [];
|
||||
$values = $daten['values'] ?? [];
|
||||
|
||||
isValidColumn($table, $columns, $erlaubteSchema);
|
||||
|
||||
if (count($columns) !== count($values)) {
|
||||
throw new InvalidArgumentException('Anzahl der Werte passt nicht zu den Spalten');
|
||||
}
|
||||
|
||||
$columnsSql = implode(', ', array_map(fn($c) => "`$c`", $columns));
|
||||
$result = DatabaseInsert($table, $columnsSql, ...$values);
|
||||
echo json_encode(['success' => $result]);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'show': {
|
||||
$columns = $daten['columns'] ?? [];
|
||||
$conditions = $daten['conditions'] ?? [];
|
||||
|
||||
if (empty($columns)) {
|
||||
$columns = $erlaubteSchema[$table];
|
||||
} else {
|
||||
isValidColumn($table, $columns, $erlaubteSchema);
|
||||
}
|
||||
|
||||
$columnsSql = implode(', ', array_map(fn($c) => "`$c`", $columns));
|
||||
[$whereSql, $values] = buildWhere($conditions, $table, $erlaubteSchema, $validOperations);
|
||||
|
||||
$result = DatabaseShow($table, $columnsSql, $whereSql, ...$values);
|
||||
echo json_encode(['success' => true, 'entries' => array_values($result ?? [])]);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'update': {
|
||||
$updates = $daten['updates'] ?? [];
|
||||
$conditions = $daten['conditions'] ?? [];
|
||||
|
||||
if (empty($conditions)) {
|
||||
throw new InvalidArgumentException('WHERE-Bedingung ist bei UPDATE Pflicht');
|
||||
}
|
||||
|
||||
isValidColumn($table, array_keys($updates), $erlaubteSchema);
|
||||
|
||||
$setParts = [];
|
||||
$setValues = [];
|
||||
foreach ($updates as $col => $val) {
|
||||
$setParts[] = "`$col` = ?";
|
||||
$setValues[] = $val;
|
||||
}
|
||||
$setSql = implode(', ', $setParts);
|
||||
|
||||
[$whereSql, $whereValues] = buildWhere($conditions, $table, $erlaubteSchema, $validOperations);
|
||||
|
||||
$result = DatabaseUpdate($table, $setSql, $whereSql, ...array_merge($setValues, $whereValues));
|
||||
echo json_encode(['success' => $result]);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'delete': {
|
||||
$conditions = $daten['conditions'] ?? [];
|
||||
|
||||
if (empty($conditions)) {
|
||||
throw new InvalidArgumentException('WHERE-Bedingung ist bei DELETE Pflicht');
|
||||
}
|
||||
|
||||
[$whereSql, $values] = buildWhere($conditions, $table, $erlaubteSchema, $validOperations);
|
||||
|
||||
$result = DatabaseDelete($table, $whereSql, ...$values);
|
||||
echo json_encode(['success' => $result]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException("Unbekannte Aktion: $action");
|
||||
}
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
error_log($e->getMessage());
|
||||
echo json_encode(['error' => 'Serverfehler']);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => '',
|
||||
'secure' => false,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax'
|
||||
]);
|
||||
session_start();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
if (empty($_SESSION['user_logged_in'])) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Nicht eingeloggt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($_SESSION['is_admin'])) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Keine Berechtigung']);
|
||||
exit;
|
||||
}
|
||||
|
||||
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../../../controller/NotifyController.php');
|
||||
|
||||
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $daten['action'] ?? '';
|
||||
|
||||
try {
|
||||
$notificationCtl = new Notification();
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'get_entries': {
|
||||
$entries = $notificationCtl->getAllMessages();
|
||||
|
||||
foreach ($entries as &$entry) {
|
||||
$entry['GESEHEN_ANZAHL'] = $notificationCtl->countSeenBy((int)$entry['ID']);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'entries' => array_values($entries)]);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'add_entry': {
|
||||
$titel = trim($daten['titel'] ?? '');
|
||||
$nachricht = trim($daten['nachricht'] ?? '');
|
||||
|
||||
if ($titel === '' || $nachricht === '') {
|
||||
throw new InvalidArgumentException('Titel und Nachricht erforderlich');
|
||||
}
|
||||
|
||||
$notificationCtl->createMessage($titel, $nachricht);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'edit_entry': {
|
||||
$id = $daten['id'] ?? null;
|
||||
$titel = trim($daten['titel'] ?? '');
|
||||
$nachricht = trim($daten['nachricht'] ?? '');
|
||||
|
||||
if (!is_numeric($id)) {
|
||||
throw new InvalidArgumentException('Ungültige ID');
|
||||
}
|
||||
if ($titel === '' || $nachricht === '') {
|
||||
throw new InvalidArgumentException('Titel und Nachricht erforderlich');
|
||||
}
|
||||
|
||||
$notificationCtl->updateMessage((int)$id, $titel, $nachricht);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'delete_entry': {
|
||||
$id = $daten['id'] ?? null;
|
||||
|
||||
if (!is_numeric($id)) {
|
||||
throw new InvalidArgumentException('Ungültige ID');
|
||||
}
|
||||
|
||||
$notificationCtl->deleteMessage((int)$id);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException("Unbekannte Aktion: $action");
|
||||
}
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
error_log($e->getMessage());
|
||||
echo json_encode(['error' => 'Serverfehler']);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => '',
|
||||
'secure' => false,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax'
|
||||
]);
|
||||
session_start();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
if (empty($_SESSION['user_logged_in'])) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Nicht eingeloggt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../../../controller/NotifyController.php');
|
||||
|
||||
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $daten['action'] ?? '';
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
|
||||
case 'mark_seen': {
|
||||
$nachrichtId = $daten['nachricht_id'] ?? null;
|
||||
|
||||
if (!is_numeric($nachrichtId)) {
|
||||
throw new InvalidArgumentException('Ungültige Nachrichten-ID');
|
||||
}
|
||||
|
||||
$notificationCtl = new Notification();
|
||||
$notificationCtl->markAsSeen((int)$nachrichtId);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException("Unbekannte Aktion: $action");
|
||||
}
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
error_log($e->getMessage());
|
||||
echo json_encode(['error' => 'Serverfehler']);
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
session_start();
|
||||
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $daten['action'] ?? '';
|
||||
$username = $_SESSION['username'] ?? 'Gast';
|
||||
|
||||
$erlaubteIntervalle = ['daily' => '+1 day', 'weekly' => '+1 week', 'monthly' => '+1 month'];
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
|
||||
// ---------- Termine laden (nur die eigenen, serienweise gruppiert) ----------
|
||||
case 'get_entries': {
|
||||
$termine = DatabaseShow(
|
||||
"Termine",
|
||||
"MAX(ID) as ID, MIN(DATUM) as DATUM, ZEIT, NAME, NOTIZ, ERSTELLT_VON, SERIEN_ID, INTERVALL, SERIEN_ENDE",
|
||||
"ERSTELLT_VON = ? GROUP BY IFNULL(SERIEN_ID, ID) ORDER BY DATUM ASC, ZEIT ASC",
|
||||
$username
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true, 'entries' => array_values($termine ?? [])]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Neuen Termin (einzeln oder als Serie) anlegen ----------
|
||||
case 'add_termin': {
|
||||
$datum = trim($daten['datum'] ?? '');
|
||||
$zeit = trim($daten['zeit'] ?? '');
|
||||
$name = trim($daten['name'] ?? '');
|
||||
$notiz = trim($daten['notiz'] ?? '');
|
||||
$wiederholung = $daten['wiederholung'] ?? 'none';
|
||||
$enddatum = trim($daten['enddatum'] ?? '');
|
||||
|
||||
if ($datum === '' || $zeit === '' || $name === '') {
|
||||
throw new InvalidArgumentException('Bitte alle Pflichtfelder ausfüllen.');
|
||||
}
|
||||
|
||||
// ----- Serientermin -----
|
||||
if ($wiederholung !== 'none' && $enddatum !== '') {
|
||||
|
||||
if (!isset($erlaubteIntervalle[$wiederholung])) {
|
||||
throw new InvalidArgumentException('Ungültiges Wiederholungsintervall.');
|
||||
}
|
||||
|
||||
$currentDate = new DateTime($datum);
|
||||
$endDate = new DateTime($enddatum);
|
||||
|
||||
if ($endDate < $currentDate) {
|
||||
throw new InvalidArgumentException('Das Enddatum darf nicht vor dem Startdatum liegen.');
|
||||
}
|
||||
|
||||
$modifier = $erlaubteIntervalle[$wiederholung];
|
||||
$serienId = uniqid('serie_', true);
|
||||
$insertedCount = 0;
|
||||
|
||||
while ($currentDate <= $endDate) {
|
||||
DatabaseInsert(
|
||||
"Termine",
|
||||
"DATUM, ZEIT, NAME, NOTIZ, ERSTELLT_VON, SERIEN_ID, INTERVALL, SERIEN_ENDE",
|
||||
$currentDate->format('Y-m-d'),
|
||||
$zeit,
|
||||
$name,
|
||||
$notiz,
|
||||
$username,
|
||||
$serienId,
|
||||
$wiederholung,
|
||||
$enddatum
|
||||
);
|
||||
$insertedCount++;
|
||||
$currentDate->modify($modifier);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => "Serientermin ($insertedCount Termine) erfolgreich eingetragen!"]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ----- Einzeltermin -----
|
||||
DatabaseInsert(
|
||||
"Termine",
|
||||
"DATUM, ZEIT, NAME, NOTIZ, ERSTELLT_VON",
|
||||
$datum,
|
||||
$zeit,
|
||||
$name,
|
||||
$notiz,
|
||||
$username
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Termin erfolgreich eingetragen!']);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Einzelnen Termin löschen ----------
|
||||
case 'delete_termin': {
|
||||
$id = $daten['id'] ?? null;
|
||||
|
||||
if (!is_numeric($id)) {
|
||||
throw new InvalidArgumentException('Ungültige ID');
|
||||
}
|
||||
|
||||
// Zusätzlich ERSTELLT_VON prüfen, damit niemand fremde Termine per ID löschen kann
|
||||
DatabaseDelete("Termine", "ID = ? AND ERSTELLT_VON = ?", (int)$id, $username);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Termin erfolgreich gelöscht!']);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Komplette Serie löschen ----------
|
||||
case 'delete_serie': {
|
||||
$serienId = trim($daten['serienId'] ?? '');
|
||||
|
||||
if ($serienId === '') {
|
||||
throw new InvalidArgumentException('Ungültige Serien-ID');
|
||||
}
|
||||
|
||||
DatabaseDelete("Termine", "SERIEN_ID = ? AND ERSTELLT_VON = ?", $serienId, $username);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Komplette Terminserie erfolgreich gelöscht!']);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException("Unbekannte Aktion: $action");
|
||||
}
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
error_log($e->getMessage());
|
||||
echo json_encode(['error' => 'Serverfehler']);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../../../controller/RoutesController.php');
|
||||
include_once(__DIR__ . '/../../../modules/Mail.php');
|
||||
|
||||
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $daten['action'] ?? '';
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
|
||||
// ---------- Liste laden (nur eigene Tickets) ----------
|
||||
case 'get_entries': {
|
||||
$entries = DatabaseShow(
|
||||
"Tickets",
|
||||
"ID, TITEL, BESCHREIBUNG, STATUS",
|
||||
"ERSTELLER = ?",
|
||||
((new getSpecificData())->getUsername()),
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true, 'entries' => array_values($entries ?? [])]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Neues Ticket anlegen ----------
|
||||
case 'add_entry': {
|
||||
foreach (['titel', 'beschreibung'] as $feld) {
|
||||
if (empty($daten[$feld])) {
|
||||
throw new InvalidArgumentException("Pflichtfeld fehlt: $feld");
|
||||
}
|
||||
}
|
||||
|
||||
$titel = $daten['titel'];
|
||||
$beschreibung = $daten['beschreibung'];
|
||||
$user = ucfirst(strtolower((new getSpecificData())->getUsername()));
|
||||
|
||||
DatabaseInsert(
|
||||
"Tickets",
|
||||
"TITEL, BESCHREIBUNG, STATUS, ERSTELLER",
|
||||
$titel,
|
||||
$beschreibung,
|
||||
"OFFEN",
|
||||
$user,
|
||||
);
|
||||
|
||||
sendMail(
|
||||
"Neues Ticket von $user",
|
||||
"Titel: $titel\nBeschreibung: $beschreibung",
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true,
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Ticket löschen ----------
|
||||
case 'delete_entry': {
|
||||
$id = $daten['id'] ?? null;
|
||||
|
||||
if (!is_numeric($id)) {
|
||||
throw new InvalidArgumentException('Ungültige ID');
|
||||
}
|
||||
|
||||
DatabaseDelete("Tickets", "ID = ?", (int)$id);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException("Unbekannte Aktion: $action");
|
||||
}
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
error_log($e->getMessage());
|
||||
echo json_encode(['error' => 'Serverfehler']);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => '',
|
||||
'secure' => false,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax'
|
||||
]);
|
||||
session_start();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
if (empty($_SESSION['user_logged_in'])) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Nicht eingeloggt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../../../controller/RoutesController.php');
|
||||
include_once(__DIR__ . '/../../../modules/Mail.php');
|
||||
|
||||
$allowedStatuses = ['OFFEN', 'DEPLOYED', 'ABGELEHNT', 'ANGENOMMEN'];
|
||||
|
||||
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $daten['action'] ?? '';
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
|
||||
// ---------- Alle Tickets laden (Verwaltungsansicht) ----------
|
||||
case 'get_entries': {
|
||||
$entries = DatabaseShow(
|
||||
"Tickets",
|
||||
"ID, TITEL, BESCHREIBUNG, STATUS, ERSTELLER",
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true, 'entries' => array_values($entries ?? [])]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Status ändern ----------
|
||||
case 'edit_entry': {
|
||||
$id = $daten['id'] ?? null;
|
||||
$status = $daten['status'] ?? '';
|
||||
|
||||
if (!is_numeric($id)) {
|
||||
throw new InvalidArgumentException('Ungültige ID');
|
||||
}
|
||||
if (!in_array($status, $allowedStatuses, true)) {
|
||||
throw new InvalidArgumentException('Ungültiger Status');
|
||||
}
|
||||
|
||||
DatabaseUpdate(
|
||||
"Tickets",
|
||||
"STATUS = ?",
|
||||
"ID = ?",
|
||||
$status,
|
||||
(int)$id
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Ticket löschen ----------
|
||||
case 'delete_entry': {
|
||||
$id = $daten['id'] ?? null;
|
||||
|
||||
if (!is_numeric($id)) {
|
||||
throw new InvalidArgumentException('Ungültige ID');
|
||||
}
|
||||
|
||||
DatabaseDelete("Tickets", "ID = ?", (int)$id);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException("Unbekannte Aktion: $action");
|
||||
}
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
error_log($e->getMessage());
|
||||
echo json_encode(['error' => 'Serverfehler']);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $daten['action'] ?? '';
|
||||
|
||||
/**
|
||||
* Baut die WHERE-Klausel serverseitig. STATUS IS NULL ist FEST verdrahtet
|
||||
* und niemals durch den Client beeinflussbar -- das ist die eigentliche
|
||||
* Geschäftsregel dieser Seite (nur "aktive" Vergaben werden angezeigt).
|
||||
*/
|
||||
function baueVergabeWhere(string $searchWer, string $searchWas, array &$values): string {
|
||||
$parts = ["STATUS IS NULL"];
|
||||
|
||||
if ($searchWer !== '') {
|
||||
$parts[] = "WER LIKE ?";
|
||||
$values[] = '%' . $searchWer . '%';
|
||||
}
|
||||
if ($searchWas !== '') {
|
||||
$parts[] = "WAS LIKE ?";
|
||||
$values[] = '%' . $searchWas . '%';
|
||||
}
|
||||
|
||||
return implode(' AND ', $parts);
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
|
||||
// ---------- Liste laden (mit optionalem Filter) ----------
|
||||
case 'get_entries': {
|
||||
$searchWer = trim($daten['searchWer'] ?? '');
|
||||
$searchWas = trim($daten['searchWas'] ?? '');
|
||||
|
||||
$values = [];
|
||||
$where = baueVergabeWhere($searchWer, $searchWas, $values);
|
||||
|
||||
$entries = DatabaseShow("Vergabe", "ID, WAS, WER, WANN, RUECKGABE", $where, ...$values);
|
||||
|
||||
echo json_encode(['success' => true, 'entries' => array_values($entries ?? [])]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Autocomplete-Vorschläge (alle bekannten WAS/WER, auch zurückgegebene) ----------
|
||||
case 'get_suggestions': {
|
||||
$alle = DatabaseShow("Vergabe");
|
||||
|
||||
$was = [];
|
||||
$wer = [];
|
||||
foreach ($alle as $entry) {
|
||||
if (!empty($entry['WAS'])) $was[$entry['WAS']] = true;
|
||||
if (!empty($entry['WER'])) $wer[$entry['WER']] = true;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'was' => array_keys($was),
|
||||
'wer' => array_keys($wer),
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Neue Vergabe anlegen ----------
|
||||
case 'add_entry': {
|
||||
foreach (['was', 'wer', 'wann', 'rueckgabe'] as $feld) {
|
||||
if (empty($daten[$feld])) {
|
||||
throw new InvalidArgumentException("Pflichtfeld fehlt: $feld");
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseInsert(
|
||||
"Vergabe",
|
||||
"WAS, WER, WANN, RUECKGABE",
|
||||
$daten['was'],
|
||||
$daten['wer'],
|
||||
$daten['wann'],
|
||||
$daten['rueckgabe']
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Vergabe als zurückgegeben markieren (Soft-Delete) ----------
|
||||
case 'return_entry': {
|
||||
$id = $daten['id'] ?? null;
|
||||
|
||||
if (!is_numeric($id)) {
|
||||
throw new InvalidArgumentException('Ungültige ID');
|
||||
}
|
||||
|
||||
DatabaseUpdate("Vergabe", "STATUS = ?", "ID = ?", 1, (int)$id);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException("Unbekannte Aktion: $action");
|
||||
}
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
error_log($e->getMessage());
|
||||
echo json_encode(['error' => 'Serverfehler']);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
session_start();
|
||||
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $daten['action'] ?? '';
|
||||
|
||||
$erlaubteGruende = ['Abbau', 'Aufbau', 'Anderes'];
|
||||
|
||||
function istBerechtigt(): bool {
|
||||
$username = $_SESSION['username'] ?? '';
|
||||
return strtolower($username) === 'bruno';
|
||||
}
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
|
||||
case 'get_stats': {
|
||||
include_once(__DIR__ . '/../../../controller/DatabaseConnect.php');
|
||||
$conn = db_connect();
|
||||
|
||||
$minuten_berichte = 0;
|
||||
$res1 = $conn->query("SELECT SUM(OVERTIME) AS summe FROM Firmenberichte");
|
||||
if ($row = $res1->fetch_object()) { $minuten_berichte = intval($row->summe); }
|
||||
|
||||
$minuten_abbau = 0;
|
||||
$res2 = $conn->query("SELECT SUM(MINUTEN) AS summe FROM Ueberstunden WHERE GRUND = 'Abbau'");
|
||||
if ($row = $res2->fetch_object()) { $minuten_abbau = intval($row->summe); }
|
||||
|
||||
$minuten_aufbau = 0;
|
||||
$res3 = $conn->query("SELECT SUM(MINUTEN) AS summe FROM Ueberstunden WHERE GRUND = 'Aufbau'");
|
||||
if ($row = $res3->fetch_object()) { $minuten_aufbau = intval($row->summe); }
|
||||
|
||||
$gesamt_minuten = $minuten_berichte - $minuten_abbau + $minuten_aufbau;
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'gesamtMinuten' => $gesamt_minuten,
|
||||
'istBerechtigt' => istBerechtigt(),
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'add_overtime': {
|
||||
if (!istBerechtigt()) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Keine Berechtigung für diese Aktion']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$datum = trim($daten['datum'] ?? '');
|
||||
$minuten = $daten['minuten'] ?? null;
|
||||
$grund = trim($daten['grund'] ?? '');
|
||||
|
||||
if ($datum === '' || !is_numeric($minuten) || $grund === '') {
|
||||
throw new InvalidArgumentException('Bitte alle Pflichtfelder korrekt ausfüllen.');
|
||||
}
|
||||
|
||||
if (!in_array($grund, $erlaubteGruende, true)) {
|
||||
throw new InvalidArgumentException('Ungültiger Grund.');
|
||||
}
|
||||
|
||||
DatabaseInsert(
|
||||
"Ueberstunden",
|
||||
"DATUM, MINUTEN, GRUND",
|
||||
$datum,
|
||||
intval($minuten),
|
||||
$grund
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Eintrag erfolgreich gespeichert!']);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException("Unbekannte Aktion: $action");
|
||||
}
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
error_log($e->getMessage());
|
||||
echo json_encode(['error' => 'Serverfehler']);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||
include_once( __DIR__ . '/../../src/controller/apiController.php');
|
||||
include_once( __DIR__ . '/../../src/utils/mailer.php');
|
||||
include_once( __DIR__ . '/../../controller/DatabaseController.php');
|
||||
include_once( __DIR__ . '/../../controller/DataController.php');
|
||||
include_once( __DIR__ . '/../../controller/ApiController.php');
|
||||
include_once(__DIR__ . '/../../modules/Mail.php');
|
||||
|
||||
header("Content-Type: application/json");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||
include_once( __DIR__ . '/../../controller/DatabaseController.php' );
|
||||
include_once( __DIR__ . '/../../controller/DataController.php' );
|
||||
|
||||
header("Content-Type: application/json");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||
include_once(__DIR__ . '/../../src/controller/apiController.php');
|
||||
include_once(__DIR__ . '/../../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../../controller/DataController.php');
|
||||
include_once(__DIR__ . '/../../controller/ApiController.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||
include_once(__DIR__ . '/../../src/controller/apiController.php');
|
||||
include_once(__DIR__ . '/../../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../../controller/DataController.php');
|
||||
include_once(__DIR__ . '/../../controller/ApiController.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<?php
|
||||
|
||||
include_once( __DIR__ . '/../../src/utils/databaseMethods.php');
|
||||
include_once( __DIR__ . '/../../src/utils/requestLogger.php');
|
||||
include_once( __DIR__ . '/../../src/classes/getData.php');
|
||||
include_once(__DIR__ . '/../../src/controller/apiController.php');
|
||||
include_once(__DIR__ . '/../../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../../controller/DataController.php');
|
||||
include_once(__DIR__ . '/../../controller/ApiController.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
|
||||
@@ -6,392 +6,4 @@ $superuser = array (
|
||||
'password' => 'bBHofurUJWKzPE8Z:',
|
||||
'active' => true,
|
||||
),
|
||||
);
|
||||
|
||||
$serviceGroups = array (
|
||||
'Info' =>
|
||||
array (
|
||||
'label' => 'Home',
|
||||
'defaultOpen' => true,
|
||||
'services' =>
|
||||
array (
|
||||
0 => 'home_info',
|
||||
1 => 'links',
|
||||
3 => 'documentation',
|
||||
),
|
||||
),
|
||||
'Ausbildung' =>
|
||||
array (
|
||||
'label' => 'Ausbildung',
|
||||
'defaultOpen' => true,
|
||||
'services' =>
|
||||
array (
|
||||
0 => 'print_static',
|
||||
1 => 'zeiterfassung',
|
||||
2 => 'berichte_arbeit_gen',
|
||||
3 => 'berichte',
|
||||
),
|
||||
),
|
||||
'Logs' =>
|
||||
array (
|
||||
'label' => 'Webserver',
|
||||
'defaultOpen' => true,
|
||||
'services' =>
|
||||
array (
|
||||
0 => 'public_request_logs',
|
||||
1 => 'public_request_chart',
|
||||
2 => 'backend_request_logs',
|
||||
3 => 'backend_request_chart',
|
||||
4 => 'personal_request_logs',
|
||||
),
|
||||
),
|
||||
'System' =>
|
||||
array (
|
||||
'label' => 'Verwaltung',
|
||||
'defaultOpen' => false,
|
||||
'services' =>
|
||||
array (
|
||||
0 => 'data_info',
|
||||
1 => 'services_control',
|
||||
2 => 'user_control',
|
||||
3 => 'variablen_control',
|
||||
),
|
||||
),
|
||||
'Test' =>
|
||||
array (
|
||||
'label' => 'Dev / Test',
|
||||
'defaultOpen' => false,
|
||||
'services' =>
|
||||
array (
|
||||
0 => 'api_tester',
|
||||
1 => 'weather_chart',
|
||||
2 => 'tabellen',
|
||||
3 => 'domains',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$services = array (
|
||||
'home_info' =>
|
||||
array (
|
||||
'name' => 'Home',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'print_static' =>
|
||||
array (
|
||||
'name' => 'Berichte Drucken',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'api_tester' =>
|
||||
array (
|
||||
'name' => 'API Testing',
|
||||
'active' => true,
|
||||
'adminOnly' => true,
|
||||
),
|
||||
'weather_chart' =>
|
||||
array (
|
||||
'name' => 'Wetter Statistik',
|
||||
'active' => true,
|
||||
'adminOnly' => true,
|
||||
),
|
||||
'data_info' =>
|
||||
array (
|
||||
'name' => 'Data Info',
|
||||
'active' => true,
|
||||
'adminOnly' => true,
|
||||
),
|
||||
'links' =>
|
||||
array (
|
||||
'name' => 'Links',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'public_request_logs' =>
|
||||
array (
|
||||
'name' => 'Public Logs',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'public_request_chart' =>
|
||||
array (
|
||||
'name' => 'Public Statistik',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'backend_request_logs' =>
|
||||
array (
|
||||
'name' => 'Backend Logs',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'backend_request_chart' =>
|
||||
array (
|
||||
'name' => 'Backend Statistik',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'personal_request_logs' =>
|
||||
array (
|
||||
'name' => 'Persönliche Logs',
|
||||
'active' => true,
|
||||
'adminOnly' => true,
|
||||
),
|
||||
'services_control' =>
|
||||
array (
|
||||
'name' => 'Seiten',
|
||||
'active' => true,
|
||||
'adminOnly' => true,
|
||||
),
|
||||
'zeiterfassung' =>
|
||||
array (
|
||||
'name' => 'Zeit Erfassung',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'berichte_arbeit_gen' =>
|
||||
array (
|
||||
'name' => 'Berichte PDF',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'user_control' =>
|
||||
array (
|
||||
'name' => 'Benutzer',
|
||||
'active' => true,
|
||||
'adminOnly' => true,
|
||||
),
|
||||
'variablen_control' =>
|
||||
array (
|
||||
'name' => 'Variablen',
|
||||
'active' => true,
|
||||
'adminOnly' => true,
|
||||
),
|
||||
'tabellen' =>
|
||||
array (
|
||||
'name' => 'Tabellen',
|
||||
'active' => true,
|
||||
'adminOnly' => true,
|
||||
),
|
||||
'berichte' =>
|
||||
array (
|
||||
'name' => 'Berichte Einsehen',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'domains' =>
|
||||
array (
|
||||
'name' => 'Domains',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'tester' =>
|
||||
array (
|
||||
'name' => 'TESTER',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'documentation' =>
|
||||
array (
|
||||
'name' => 'Dokumentation',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'logout' =>
|
||||
array (
|
||||
'name' => 'Abmelden',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
);
|
||||
|
||||
$linkGroups = array (
|
||||
'info' =>
|
||||
array (
|
||||
'label' => 'Informationen',
|
||||
'color' => '#3498db',
|
||||
'defaultOpen' => false,
|
||||
),
|
||||
'tools' =>
|
||||
array (
|
||||
'label' => 'IoT',
|
||||
'color' => '#2ecc71',
|
||||
'defaultOpen' => false,
|
||||
),
|
||||
'admin' =>
|
||||
array (
|
||||
'label' => 'Administration',
|
||||
'color' => '#e74c3c',
|
||||
'defaultOpen' => false,
|
||||
),
|
||||
'devices' =>
|
||||
array (
|
||||
'label' => 'Geräte',
|
||||
'color' => '#b7db33',
|
||||
'defaultOpen' => false,
|
||||
),
|
||||
);
|
||||
|
||||
$linkButtons = array (
|
||||
0 =>
|
||||
array (
|
||||
'label' => 'Gitea',
|
||||
'url' => 'http://gitea.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'admin',
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'label' => 'HAProxy',
|
||||
'url' => 'http://stats.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'admin',
|
||||
),
|
||||
2 =>
|
||||
array (
|
||||
'label' => 'Public',
|
||||
'url' => 'https://blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'info',
|
||||
),
|
||||
3 =>
|
||||
array (
|
||||
'label' => 'NAS-1',
|
||||
'url' => 'http://nas-1.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'devices',
|
||||
),
|
||||
4 =>
|
||||
array (
|
||||
'label' => 'NAS-2',
|
||||
'url' => 'http://nas-2.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'devices',
|
||||
),
|
||||
5 =>
|
||||
array (
|
||||
'label' => 'FritzBox-1',
|
||||
'url' => 'http://fritzbox-1.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'devices',
|
||||
),
|
||||
6 =>
|
||||
array (
|
||||
'label' => 'FritzBox-2',
|
||||
'url' => 'http://fritzbox-2.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'devices',
|
||||
),
|
||||
7 =>
|
||||
array (
|
||||
'label' => 'GS1910-24',
|
||||
'url' => 'http://switch.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'admin',
|
||||
),
|
||||
8 =>
|
||||
array (
|
||||
'label' => 'NWA5121-NI Innen',
|
||||
'url' => 'http://ap-1.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'devices',
|
||||
),
|
||||
9 =>
|
||||
array (
|
||||
'label' => 'NWA5121-NI Aussen',
|
||||
'url' => 'http://ap-2.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'devices',
|
||||
),
|
||||
10 =>
|
||||
array (
|
||||
'label' => 'ZyWALL 110',
|
||||
'url' => 'http://10.233.1248',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'admin',
|
||||
),
|
||||
11 =>
|
||||
array (
|
||||
'label' => 'Ender 3 V3 KE',
|
||||
'url' => 'http://10.233.1.52',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'tools',
|
||||
),
|
||||
12 =>
|
||||
array (
|
||||
'label' => 'Ubuntu NAS',
|
||||
'url' => 'http://10.233.1.32:9000',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'tools',
|
||||
),
|
||||
13 =>
|
||||
array (
|
||||
'label' => 'Backend',
|
||||
'url' => 'https://backend.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'info',
|
||||
),
|
||||
14 =>
|
||||
array (
|
||||
'label' => 'Dev Backend',
|
||||
'url' => 'https://dev-backend.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'info',
|
||||
),
|
||||
15 =>
|
||||
array (
|
||||
'label' => 'Dev Pubic',
|
||||
'url' => 'https://dev-public.blackpi.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'info',
|
||||
),
|
||||
16 =>
|
||||
array (
|
||||
'label' => 'Local Backend',
|
||||
'url' => 'http://backend.local.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'info',
|
||||
),
|
||||
17 =>
|
||||
array (
|
||||
'label' => 'Local Pubic',
|
||||
'url' => 'http://public.local.ipv64.net',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'info',
|
||||
),
|
||||
18 =>
|
||||
array (
|
||||
'label' => 'Proxmox',
|
||||
'url' => 'https://100.88.208.86:8006',
|
||||
'newTab' => true,
|
||||
'adminOnly' => true,
|
||||
'group' => 'devices',
|
||||
),
|
||||
19 =>
|
||||
array (
|
||||
'label' => 'Flatnotes',
|
||||
'url' => 'http://10.233.1.130:8080',
|
||||
'newTab' => true,
|
||||
'adminOnly' => false,
|
||||
'group' => 'tools',
|
||||
),
|
||||
);
|
||||
@@ -1,10 +1,9 @@
|
||||
<?php
|
||||
|
||||
require(__DIR__ . '/../utils/formatter.php');
|
||||
include_once(__DIR__ . '/DatabaseController.php');
|
||||
|
||||
class ApiController {
|
||||
|
||||
use ReponseFormatter;
|
||||
class ApiController
|
||||
{
|
||||
|
||||
/**
|
||||
* method for pretty jsonsuccess output
|
||||
@@ -12,8 +11,15 @@ class ApiController {
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function jsonSuccess($data) {
|
||||
return $this->formatJson("success", $data);
|
||||
public function jsonSuccess($data)
|
||||
{
|
||||
return json_encode(
|
||||
[
|
||||
'status' => 'success',
|
||||
'data' => $data,
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22,8 +28,16 @@ class ApiController {
|
||||
* @param array $error
|
||||
* @return string
|
||||
*/
|
||||
public function jsonError ($error) {
|
||||
return $this->formatJson("failed", $error);
|
||||
public function jsonError($error)
|
||||
{
|
||||
echo json_encode(
|
||||
[
|
||||
'status' => 'failed',
|
||||
'data' => 'none',
|
||||
'error' => $error
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +45,8 @@ class ApiController {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAuthKey () {
|
||||
private function getAuthKey()
|
||||
{
|
||||
$validTokens = DatabaseShow(
|
||||
"API_Credentials",
|
||||
"TOKEN"
|
||||
@@ -47,13 +62,18 @@ class ApiController {
|
||||
* @param string $function
|
||||
* @return void
|
||||
*/
|
||||
public function urlAuth ($token, $function = '') {
|
||||
public function urlAuth($token, $function = '')
|
||||
{
|
||||
|
||||
$validTokens = $this->getAuthKey();
|
||||
if (!in_array($token, $validTokens)) {
|
||||
http_response_code(401);
|
||||
$error = json_encode([
|
||||
"error" => "Unauthorized",
|
||||
"debug_received_token" => $token,
|
||||
]);
|
||||
|
||||
$error = $this->formatJson("error", $token);
|
||||
echo $this->jsonError($error);
|
||||
|
||||
DatabaseInsert(
|
||||
"API_LOGS",
|
||||
@@ -75,7 +95,8 @@ class ApiController {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function requestSave($function = '') {
|
||||
public function requestSave($function = '')
|
||||
{
|
||||
DatabaseInsert(
|
||||
"API_LOGS",
|
||||
"CLIENT_IP, TOKEN, API, FUNKTION, JSON",
|
||||
@@ -91,7 +112,8 @@ class ApiController {
|
||||
* create new api token
|
||||
*
|
||||
*/
|
||||
private function createToken() {
|
||||
private function createToken()
|
||||
{
|
||||
return bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
@@ -100,8 +122,9 @@ class ApiController {
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveToken() {
|
||||
try {
|
||||
public function saveToken()
|
||||
{
|
||||
try {
|
||||
DatabaseInsert(
|
||||
"API_Credentials",
|
||||
"TOKEN, BENUTZER",
|
||||
@@ -113,4 +136,4 @@ class ApiController {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-38
@@ -1,39 +1,39 @@
|
||||
<?php
|
||||
|
||||
class cookieController {
|
||||
|
||||
public function setCookie(string $CookieName, int $CookieValue, int $CookieTime) {
|
||||
setcookie($CookieName, $CookieValue, time()+($CookieTime*60));
|
||||
}
|
||||
|
||||
public function readCookieValue(string $CookieName) {
|
||||
if (isset($_COOKIE[$CookieName])) {
|
||||
return $_COOKIE[$CookieName];
|
||||
}
|
||||
return 'Cookie not set';
|
||||
}
|
||||
|
||||
public function DeleteCookie(string $CookieName) {
|
||||
setcookie($CookieName, "", time()-(3600*24));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// USE:
|
||||
|
||||
/**
|
||||
* Cookie setzen:
|
||||
* $cookieManager = new cookieController();
|
||||
* $cookieManager->SetCookie("USER", "7", 60);
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cookie auslesen:
|
||||
* $cookieValue = $cookieManager->readCookieValueCookie("USER");
|
||||
* echo $cookieValue;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cookie loeschen:
|
||||
* $cookieManager->DeleteCookie("USER");
|
||||
<?php
|
||||
|
||||
class cookieController {
|
||||
|
||||
public function setCookie(string $CookieName, int $CookieValue, int $CookieTime) {
|
||||
setcookie($CookieName, $CookieValue, time()+($CookieTime*60));
|
||||
}
|
||||
|
||||
public function readCookieValue(string $CookieName) {
|
||||
if (isset($_COOKIE[$CookieName])) {
|
||||
return $_COOKIE[$CookieName];
|
||||
}
|
||||
return 'Cookie not set';
|
||||
}
|
||||
|
||||
public function DeleteCookie(string $CookieName) {
|
||||
setcookie($CookieName, "", time()-(3600*24));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// USE:
|
||||
|
||||
/**
|
||||
* Cookie setzen:
|
||||
* $cookieManager = new cookieController();
|
||||
* $cookieManager->SetCookie("USER", "7", 60);
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cookie auslesen:
|
||||
* $cookieValue = $cookieManager->readCookieValueCookie("USER");
|
||||
* echo $cookieValue;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cookie loeschen:
|
||||
* $cookieManager->DeleteCookie("USER");
|
||||
*/
|
||||
@@ -1,262 +1,240 @@
|
||||
<?php
|
||||
|
||||
class getSpecificData {
|
||||
|
||||
public function getWeather() {
|
||||
$url = "https://api.openweathermap.org/data/2.5/weather?q=Jena,DE&APPID=5bc3e1a438c58fca444c4370ac0517b2&units=metric";
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if(curl_errno($ch)) {
|
||||
echo "cURL Fehler: " . curl_error($ch);
|
||||
exit;
|
||||
}
|
||||
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200) {
|
||||
$data = json_decode($response, true);
|
||||
|
||||
return [
|
||||
'aktuell' => $data['main']['temp'],
|
||||
'min' => $data['main']['temp_min'],
|
||||
'max' => $data['main']['temp_max'],
|
||||
'gefuehlt' => $data['main']['feels_like']
|
||||
];
|
||||
} else {
|
||||
return "API Fehler. HTTP Code: " . $httpCode;
|
||||
}
|
||||
}
|
||||
|
||||
public function getUserAgent(): string {
|
||||
return $_SERVER['HTTP_USER_AGENT'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getRequestURL(): string {
|
||||
return $_SERVER['REQUEST_URI'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getCookie(string $name): ?string {
|
||||
return $_COOKIE[$name] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getSessionID(): string {
|
||||
return $session_id ?? 'Unbekannt ';
|
||||
}
|
||||
|
||||
public function getServerPort(): string {
|
||||
return $_SERVER['SERVER_PORT'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getClientPort(): string {
|
||||
return $_SERVER['REMOTE_PORT'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getDocumentRoot(): string {
|
||||
return $_SERVER['DOCUMENT_ROOT'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getQuery(): string {
|
||||
return $_SERVER['QUERY_STRING'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getRequestMethod(): string {
|
||||
return $_SERVER['REQUEST_METHOD'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getCurrentDir(): string {
|
||||
return __DIR__ ;
|
||||
}
|
||||
|
||||
public function getReferer(): string {
|
||||
return $_SERVER['HTTP_REFERER'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getWebsite(): string {
|
||||
return $_SERVER['HTTP_HOST'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getDatabase(): string {
|
||||
$conn = db_connect();
|
||||
return $conn->host_info;
|
||||
}
|
||||
|
||||
public function getLoggingStatus($variable) {
|
||||
$status = DatabaseShow(
|
||||
"Variablen",
|
||||
"WERT",
|
||||
"VARIABLENNAME = ?",
|
||||
$variable
|
||||
);
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function getServerName(): string {
|
||||
$Servername = $_SERVER['SERVER_NAME'] ?? 'Unbekannt';
|
||||
|
||||
if (in_array($Servername, ['Unbekannt', '0.0.0.0', '127.0.0.1'])) {
|
||||
return "Lokal";
|
||||
} else {
|
||||
return $Servername;
|
||||
}
|
||||
}
|
||||
|
||||
public function getServerIP(): string {
|
||||
$ServerIP = $_SERVER['SERVER_ADDR'] ?? 'Unbekannt';
|
||||
|
||||
if (in_array($ServerIP, ['Unbekannt', '0.0.0.0', '127.0.0.1'])) {
|
||||
return "Lokal";
|
||||
} else {
|
||||
return $ServerIP;
|
||||
}
|
||||
}
|
||||
|
||||
public function getClientIP(): string {
|
||||
$xff = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null;
|
||||
|
||||
if ($xff) {
|
||||
$ips = array_map('trim', explode(',', $xff));
|
||||
$clientIp = $ips[0];
|
||||
return $clientIp;
|
||||
} else {
|
||||
return $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
}
|
||||
|
||||
public function getRequestTime(): string {
|
||||
date_default_timezone_set('Europe/Berlin');
|
||||
$timestamp = $_SERVER['REQUEST_TIME'] ?? time();
|
||||
return date('Y-m-d H:i:s', $timestamp);
|
||||
}
|
||||
|
||||
function getGeoIP($ClientIP) {
|
||||
if (in_array($ClientIP, ['0.0.0.0', '127.0.0.1', '172.30.0.1'])) {
|
||||
return "Lokal";
|
||||
} elseif (in_array($ClientIP, ['Unbekannt', '0.0.0.0', '127.0.0.1'])) {
|
||||
return "Lokal";
|
||||
} else {
|
||||
$response = file_get_contents("http://ip-api.com/json/{$ClientIP}?fields=country,countryCode");
|
||||
$data = json_decode($response);
|
||||
return $data->country;
|
||||
}
|
||||
}
|
||||
|
||||
function getWhitelistedIPs() {
|
||||
include_once(__DIR__ . '/../utils/databaseMethods.php');
|
||||
$data = DatabaseShow(
|
||||
"Whitelisted",
|
||||
"`IP-ADDRESSE`"
|
||||
);
|
||||
|
||||
$results = [];
|
||||
foreach ($data as $row) {
|
||||
$results[] =$row['IP-ADDRESSE'];
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
function getServerNode($ServerIP) {
|
||||
$nodes = [
|
||||
'node1' => '172.30.0.11',
|
||||
'node1' => '172.30.0.21',
|
||||
|
||||
'node2' => '172.30.0.12',
|
||||
'node2' => '172.30.0.22',
|
||||
|
||||
'node3' => '172.30.0.13',
|
||||
'node3' => '172.30.0.23',
|
||||
|
||||
'node4' => '172.30.0.14',
|
||||
'node4' => '172.30.0.24',
|
||||
'Lokal' => 'Lokal',
|
||||
];
|
||||
|
||||
foreach ($nodes as $node => $ip) {
|
||||
if ($ServerIP == $ip) {
|
||||
return $node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getClientStatus($ClientIP) {
|
||||
if (in_array($ClientIP, $this->getWhitelistedIPs())) {
|
||||
return "Trusted";
|
||||
}
|
||||
return "Untrusted";
|
||||
}
|
||||
|
||||
function getIsAdmin(){
|
||||
return $_SESSION['is_admin'] ?? false;
|
||||
}
|
||||
|
||||
function getIsSuperuser(){
|
||||
return $_SESSION['is_superuser'] ?? false;
|
||||
}
|
||||
|
||||
function getUsername(){
|
||||
return $_SESSION['username'] ?? 'Gast';
|
||||
}
|
||||
|
||||
public function getAllData(): array {
|
||||
return [
|
||||
'Server - Document Root' => $this->getDocumentRoot(),
|
||||
'Server - Current Dir' => $this->getCurrentDir(),
|
||||
'Server - Name' => $this->getServerName(),
|
||||
'Server - IP' => $this->getServerIP(),
|
||||
'Server - Port' => $this->getServerPort(),
|
||||
'Server - Node' => $this->getServerNode($this->getServerIP()),
|
||||
'Server - Database' => $this->getDatabase(),
|
||||
|
||||
|
||||
'Request - URL' => $this->getRequestURL(),
|
||||
'Request - Method' => $this->getRequestMethod(),
|
||||
'Request - Query' => $this->getQuery(),
|
||||
'Request - Referer' => $this->getReferer(),
|
||||
'Request - Time' => $this->getRequestTime(),
|
||||
|
||||
|
||||
'Client - IP' => $this->getClientIP(),
|
||||
'Client - Port' => $this->getClientPort(),
|
||||
'Client - Country' => $this->getGeoIP($this->getClientIP()),
|
||||
'Client - User Agent' => $this->getUserAgent(),
|
||||
'Client - Cookie USER' => $this->getCookie('USER'),
|
||||
'Client - Cookie SESSSRV' => $this->getCookie('SESSSRV'),
|
||||
'Client - Session ID' => $this->getSessionID(),
|
||||
|
||||
|
||||
'Access - Whitelisted' => $this->getClientStatus($this->getClientIP()),
|
||||
'Access - Whitelisted IPs' => $this->getWhitelistedIPs(),
|
||||
|
||||
|
||||
'User - Username' => $this->getUsername(),
|
||||
'User - Is Admin' => $this->getIsAdmin(),
|
||||
'User - Is Superuser' => $this->getIsSuperuser(),
|
||||
|
||||
'System - Website' => $this->getWebsite(),
|
||||
'Logging - Public' => $this->getLoggingStatus('GENERAL_PUBLIC_LOGGING')[0]['WERT'],
|
||||
'Logging - Public DEV' => $this->getLoggingStatus('DEV_PUBLIC_LOGGING')[0]['WERT'],
|
||||
'Logging - Backend' => $this->getLoggingStatus('GENERAL_BACKEND_LOGGING')[0]['WERT'],
|
||||
'Logging - Backend DEV' => $this->getLoggingStatus('DEV_BACKEND_LOGGING')[0]['WERT'],
|
||||
];
|
||||
}
|
||||
|
||||
public function ShowAllData(): void {
|
||||
$SpecificData = (new getSpecificData())->getAllData();
|
||||
|
||||
echo "<pre>";
|
||||
foreach ($SpecificData as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$value = implode(', ', $value);
|
||||
}
|
||||
printf("%-22s : %s\n", $key, $value);
|
||||
}
|
||||
echo "</pre>";
|
||||
}
|
||||
<?php
|
||||
|
||||
class getSpecificData {
|
||||
|
||||
public function getWeather() {
|
||||
$url = "https://api.openweathermap.org/data/2.5/weather?q=Jena,DE&APPID=5bc3e1a438c58fca444c4370ac0517b2&units=metric";
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if(curl_errno($ch)) {
|
||||
echo "cURL Fehler: " . curl_error($ch);
|
||||
exit;
|
||||
}
|
||||
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200) {
|
||||
$data = json_decode($response, true);
|
||||
|
||||
return [
|
||||
'aktuell' => $data['main']['temp'],
|
||||
'min' => $data['main']['temp_min'],
|
||||
'max' => $data['main']['temp_max'],
|
||||
'gefuehlt' => $data['main']['feels_like']
|
||||
];
|
||||
} else {
|
||||
return "API Fehler. HTTP Code: " . $httpCode;
|
||||
}
|
||||
}
|
||||
|
||||
public function getUserAgent(): string {
|
||||
return $_SERVER['HTTP_USER_AGENT'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getRequestURL(): string {
|
||||
return $_SERVER['REQUEST_URI'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getCookie(string $name): ?string {
|
||||
return $_COOKIE[$name] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getSessionID(): string {
|
||||
return $session_id ?? 'Unbekannt ';
|
||||
}
|
||||
|
||||
public function getServerPort(): string {
|
||||
return $_SERVER['SERVER_PORT'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getClientPort(): string {
|
||||
return $_SERVER['REMOTE_PORT'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getDocumentRoot(): string {
|
||||
return $_SERVER['DOCUMENT_ROOT'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getQuery(): string {
|
||||
return $_SERVER['QUERY_STRING'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getRequestMethod(): string {
|
||||
return $_SERVER['REQUEST_METHOD'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getCurrentDir(): string {
|
||||
return __DIR__ ;
|
||||
}
|
||||
|
||||
public function getReferer(): string {
|
||||
return $_SERVER['HTTP_REFERER'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getWebsite(): string {
|
||||
return $_SERVER['HTTP_HOST'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getDatabase(): string {
|
||||
$conn = db_connect();
|
||||
return $conn->host_info;
|
||||
}
|
||||
|
||||
public function getLoggingStatus($variable) {
|
||||
$status = DatabaseShow(
|
||||
"Variablen",
|
||||
"WERT",
|
||||
"VARIABLENNAME = ?",
|
||||
$variable
|
||||
);
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function getServerName(): string {
|
||||
$Servername = $_SERVER['SERVER_NAME'] ?? 'Unbekannt';
|
||||
|
||||
if (in_array($Servername, ['Unbekannt', '0.0.0.0', '127.0.0.1'])) {
|
||||
return "Lokal";
|
||||
} else {
|
||||
return $Servername;
|
||||
}
|
||||
}
|
||||
|
||||
public function getServerIP(): string {
|
||||
return $_SERVER['SERVER_ADDR'] ?? 'Unbekannt';
|
||||
}
|
||||
|
||||
public function getClientIP(): string
|
||||
{
|
||||
if (PHP_SAPI === 'cli') {
|
||||
return 'CLI';
|
||||
}
|
||||
|
||||
$xff = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null;
|
||||
|
||||
if (!empty($xff)) {
|
||||
$ips = array_map('trim', explode(',', $xff));
|
||||
return $ips[0];
|
||||
}
|
||||
|
||||
return $_SERVER['REMOTE_ADDR'] ?? 'UNKNOWN';
|
||||
}
|
||||
|
||||
public function getRequestTime(): string {
|
||||
date_default_timezone_set('Europe/Berlin');
|
||||
$timestamp = $_SERVER['REQUEST_TIME'] ?? time();
|
||||
return date('Y-m-d H:i:s', $timestamp);
|
||||
}
|
||||
|
||||
function getGeoIP($ClientIP) {
|
||||
if (in_array($ClientIP, ['0.0.0.0', '127.0.0.1', '172.30.0.1'])) {
|
||||
return "Lokal";
|
||||
} elseif (in_array($ClientIP, ['Unbekannt', '0.0.0.0', '127.0.0.1'])) {
|
||||
return "Lokal";
|
||||
} else {
|
||||
$response = file_get_contents("http://ip-api.com/json/{$ClientIP}?fields=country,countryCode");
|
||||
$data = json_decode($response);
|
||||
return $data->country;
|
||||
}
|
||||
}
|
||||
|
||||
function getWhitelistedIPs() {
|
||||
include_once 'DatabaseController.php';
|
||||
$data = DatabaseShow(
|
||||
"Whitelisted",
|
||||
"`IP-ADDRESSE`"
|
||||
);
|
||||
|
||||
$results = [];
|
||||
foreach ($data as $row) {
|
||||
$results[] =$row['IP-ADDRESSE'];
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
function getServerNode($ServerIP) {
|
||||
return $ServerIP;
|
||||
}
|
||||
|
||||
function getClientStatus($ClientIP) {
|
||||
if (in_array($ClientIP, $this->getWhitelistedIPs())) {
|
||||
return "Trusted";
|
||||
}
|
||||
return "Untrusted";
|
||||
}
|
||||
|
||||
function getIsAdmin(){
|
||||
return $_SESSION['is_admin'] ?? false;
|
||||
}
|
||||
|
||||
function getIsSuperuser(){
|
||||
return $_SESSION['is_superuser'] ?? false;
|
||||
}
|
||||
|
||||
function getUsername(){
|
||||
return $_SESSION['username'] ?? 'Gast';
|
||||
}
|
||||
|
||||
public function getAllData(): array {
|
||||
return [
|
||||
'Server - Document Root' => $this->getDocumentRoot(),
|
||||
'Server - Current Dir' => $this->getCurrentDir(),
|
||||
'Server - Name' => $this->getServerName(),
|
||||
'Server - IP' => $this->getServerIP(),
|
||||
'Server - Port' => $this->getServerPort(),
|
||||
'Server - Node' => $this->getServerNode($this->getServerIP()),
|
||||
'Server - Database' => $this->getDatabase(),
|
||||
|
||||
|
||||
'Request - URL' => $this->getRequestURL(),
|
||||
'Request - Method' => $this->getRequestMethod(),
|
||||
'Request - Query' => $this->getQuery(),
|
||||
'Request - Referer' => $this->getReferer(),
|
||||
'Request - Time' => $this->getRequestTime(),
|
||||
|
||||
|
||||
'Client - IP' => $this->getClientIP(),
|
||||
'Client - Port' => $this->getClientPort(),
|
||||
'Client - Country' => $this->getGeoIP($this->getClientIP()),
|
||||
'Client - User Agent' => $this->getUserAgent(),
|
||||
'Client - Cookie USER' => $this->getCookie('USER'),
|
||||
'Client - Cookie SESSSRV' => $this->getCookie('SESSSRV'),
|
||||
'Client - Session ID' => $this->getSessionID(),
|
||||
|
||||
|
||||
'Access - Whitelisted' => $this->getClientStatus($this->getClientIP()),
|
||||
'Access - Whitelisted IPs' => $this->getWhitelistedIPs(),
|
||||
|
||||
|
||||
'User - Username' => $this->getUsername(),
|
||||
'User - Is Admin' => $this->getIsAdmin(),
|
||||
'User - Is Superuser' => $this->getIsSuperuser(),
|
||||
|
||||
'System - Website' => $this->getWebsite(),
|
||||
'Logging - Public' => $this->getLoggingStatus('GENERAL_PUBLIC_LOGGING')[0]['WERT'],
|
||||
'Logging - Public DEV' => $this->getLoggingStatus('DEV_PUBLIC_LOGGING')[0]['WERT'],
|
||||
'Logging - Backend' => $this->getLoggingStatus('GENERAL_BACKEND_LOGGING')[0]['WERT'],
|
||||
'Logging - Backend DEV' => $this->getLoggingStatus('DEV_BACKEND_LOGGING')[0]['WERT'],
|
||||
];
|
||||
}
|
||||
|
||||
public function ShowAllData(): void {
|
||||
$SpecificData = (new getSpecificData())->getAllData();
|
||||
|
||||
echo "<pre>";
|
||||
foreach ($SpecificData as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$value = implode(', ', $value);
|
||||
}
|
||||
printf("%-22s : %s\n", $key, $value);
|
||||
}
|
||||
echo "</pre>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
include_once __DIR__ . '/../modules/Mail.php';
|
||||
include_once __DIR__ . '/../controller/RoutesController.php';
|
||||
include_once 'DataController.php';
|
||||
|
||||
function db_connect()
|
||||
{
|
||||
$servers = [
|
||||
'local' => [
|
||||
'hostaddr' => '127.0.0.1',
|
||||
'username' => 'worker',
|
||||
'password' => 'bBHofurUJWKzPE8Z',
|
||||
'database' => 'DEV',
|
||||
],
|
||||
'live' => [
|
||||
'hostaddr' => '152.53.159.169',
|
||||
'username' => 'worker',
|
||||
'password' => 'bBHofurUJWKzPE8Z',
|
||||
'database' => 'LIVE',
|
||||
],
|
||||
'dev' => [
|
||||
'hostaddr' => '152.53.159.169',
|
||||
'username' => 'worker',
|
||||
'password' => 'bBHofurUJWKzPE8Z',
|
||||
'database' => 'DEV',
|
||||
],
|
||||
];
|
||||
|
||||
$enviroment = 'dev';
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||
|
||||
try {
|
||||
|
||||
$conn = new mysqli(
|
||||
$servers[$enviroment]['hostaddr'],
|
||||
$servers[$enviroment]['username'],
|
||||
$servers[$enviroment]['password'],
|
||||
$servers[$enviroment]['database'],
|
||||
);
|
||||
|
||||
$conn->set_charset("utf8mb4");
|
||||
return $conn;
|
||||
} catch (mysqli_sql_exception $e) {
|
||||
|
||||
errorMail(
|
||||
"Database Connection failed",
|
||||
$e->getMessage(),
|
||||
false,
|
||||
);
|
||||
redirectToWebsite("https://mrhossa.de");
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
+292
-269
@@ -1,270 +1,293 @@
|
||||
<?php
|
||||
|
||||
function DatabaseCreateTable($table, $columnsDefinition) {
|
||||
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "CREATE TABLE IF NOT EXISTS `$table` ($columnsDefinition)";
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function DatabaseShowTables() {
|
||||
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||
$conn = db_connect();
|
||||
|
||||
$tablesResult = $conn->query("SHOW TABLES");
|
||||
if (!$tablesResult) {
|
||||
die("Fehler beim Laden der Tabellen: " . $conn->error);
|
||||
}
|
||||
|
||||
$tables = [];
|
||||
|
||||
while ($tableRow = $tablesResult->fetch_array()) {
|
||||
$tables[] = $tableRow[0];
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
function DatabaseShowTableStructure($table) {
|
||||
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||
$conn = db_connect();
|
||||
|
||||
$stmt = $conn->prepare("SHOW COLUMNS FROM `$table`");
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$columns = [];
|
||||
|
||||
while ($column = $result->fetch_assoc()) {
|
||||
$columns[] = [
|
||||
"Field" => $column["Field"],
|
||||
"Type" => $column["Type"],
|
||||
"Null" => $column["Null"],
|
||||
"Key" => $column["Key"],
|
||||
"Default" => $column["Default"],
|
||||
"Extra" => $column["Extra"]
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
function DatabaseShow($table, $columns = "*", $where = "", ...$values) {
|
||||
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "SELECT $columns FROM `$table`";
|
||||
|
||||
if (!empty($where)) {
|
||||
$sql .= " WHERE $where";
|
||||
}
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute($values)) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
$result = $stmt->get_result();
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
|
||||
function DatabaseInsert($table, $columns, ...$values) {
|
||||
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||
$conn = db_connect();
|
||||
|
||||
$placeholders = rtrim(str_repeat("?,", count($values)), ",");
|
||||
$sql = "INSERT INTO `$table` ($columns) VALUES ($placeholders)";
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute($values)) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function DatabaseDelete($table, $where, ...$values) {
|
||||
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "DELETE FROM `$table` WHERE $where";
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute($values)) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function DatabaseUpdate($table, $set, $where, ...$values) {
|
||||
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "UPDATE `$table` SET $set WHERE $where";
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute($values)) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getFKValue($conn, $table, $key, $value, $id, &$cache) {
|
||||
if ($id === null) return null;
|
||||
|
||||
$cacheKey = $table . "_" . $id;
|
||||
|
||||
if (isset($cache[$cacheKey])) {
|
||||
return $cache[$cacheKey];
|
||||
}
|
||||
|
||||
$stmt = $conn->prepare("SELECT `$value` FROM `$table` WHERE `$key` = ? LIMIT 1");
|
||||
if (!$stmt) {
|
||||
die("FK Prepare Fehler: " . $conn->error);
|
||||
}
|
||||
|
||||
$stmt->bind_param("i", $id);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->get_result()->fetch_assoc();
|
||||
$valueResult = $result[$value] ?? null;
|
||||
|
||||
$cache[$cacheKey] = $valueResult;
|
||||
|
||||
return $valueResult;
|
||||
}
|
||||
|
||||
function DatabaseShowWithFK($table, $fkMap = [], $columns = "*", $where = "", ...$values) {
|
||||
include_once(__DIR__ . '/../database/databaseConnect.php');
|
||||
$conn = db_connect();
|
||||
|
||||
$cache = [];
|
||||
|
||||
$select = is_array($columns) ? implode(", ", $columns) : $columns;
|
||||
|
||||
$sql = "SELECT $select FROM `$table`";
|
||||
if (!empty($where)) {
|
||||
$sql .= " WHERE $where";
|
||||
}
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
if (!$stmt) die("Fehler beim Prepare: " . $conn->error);
|
||||
|
||||
if (!empty($values)) {
|
||||
$stmt->execute($values);
|
||||
} else {
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
$result = $stmt->get_result();
|
||||
if (!$result) die("Fehler beim get_result: " . $conn->error);
|
||||
|
||||
$rows = $result->fetch_all(MYSQLI_ASSOC);
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
foreach ($fkMap as $fkColumn => $fkInfo) {
|
||||
|
||||
if (!isset($row[$fkColumn]) || $row[$fkColumn] === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row[$fkColumn] = getFKValue(
|
||||
$conn,
|
||||
$fkInfo['table'],
|
||||
$fkInfo['key'],
|
||||
$fkInfo['value'],
|
||||
$row[$fkColumn],
|
||||
$cache
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
"rows" => $rows
|
||||
];
|
||||
}
|
||||
|
||||
$Berufsschulberichte = [
|
||||
"FACH_S1" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S1" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S1" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S2" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S2" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S2" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S3" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S3" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S3" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S4" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S4" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S4" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S5" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S5" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S5" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S6" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S6" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S6" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S7" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S7" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S7" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S8" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S8" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S8" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
];
|
||||
|
||||
$Stundenplan = [
|
||||
"MO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"DI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"MI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"DO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"FR" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
];
|
||||
|
||||
$Berechtigungen = [
|
||||
"BENUTZER_ID" => ["table" => "Benutzer", "key" => "ID", "value" => "NAME"],
|
||||
"SEITEN_ID" => ["table" => "Backend_Seiten", "key" => "ID", "value" => "DATEINAME"]
|
||||
<?php
|
||||
|
||||
function DatabaseCreateTable($table, $columnsDefinition) {
|
||||
include_once 'DatabaseConnect.php';
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "CREATE TABLE IF NOT EXISTS `$table` ($columnsDefinition)";
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function DatabaseShowTables() {
|
||||
include_once 'DatabaseConnect.php';
|
||||
$conn = db_connect();
|
||||
|
||||
$tablesResult = $conn->query("SHOW TABLES");
|
||||
if (!$tablesResult) {
|
||||
die("Fehler beim Laden der Tabellen: " . $conn->error);
|
||||
}
|
||||
|
||||
$tables = [];
|
||||
|
||||
while ($tableRow = $tablesResult->fetch_array()) {
|
||||
$tables[] = $tableRow[0];
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
function DatabaseShowTableStructure($table) {
|
||||
include_once 'DatabaseConnect.php';
|
||||
$conn = db_connect();
|
||||
|
||||
$stmt = $conn->prepare("SHOW COLUMNS FROM `$table`");
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$columns = [];
|
||||
|
||||
while ($column = $result->fetch_assoc()) {
|
||||
$columns[] = [
|
||||
"Field" => $column["Field"],
|
||||
"Type" => $column["Type"],
|
||||
"Null" => $column["Null"],
|
||||
"Key" => $column["Key"],
|
||||
"Default" => $column["Default"],
|
||||
"Extra" => $column["Extra"]
|
||||
];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
function DatabaseShow($table, $columns = "*", $where = "", ...$values) {
|
||||
include_once 'DatabaseConnect.php';
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "SELECT $columns FROM `$table`";
|
||||
|
||||
if (!empty($where)) {
|
||||
$sql .= " WHERE $where";
|
||||
}
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute($values)) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
$result = $stmt->get_result();
|
||||
return $result->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
|
||||
function DatabaseInsert($table, $columns, ...$values) {
|
||||
include_once 'DatabaseConnect.php';
|
||||
$conn = db_connect();
|
||||
|
||||
$placeholders = rtrim(str_repeat("?,", count($values)), ",");
|
||||
$sql = "INSERT INTO `$table` ($columns) VALUES ($placeholders)";
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute($values)) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function DatabaseDelete($table, $where, ...$values) {
|
||||
include_once 'DatabaseConnect.php';
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "DELETE FROM `$table` WHERE $where";
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute($values)) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function DatabaseUpdate($table, $set, $where, ...$values) {
|
||||
include_once 'DatabaseConnect.php';
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "UPDATE `$table` SET $set WHERE $where";
|
||||
$stmt = $conn->prepare($sql);
|
||||
|
||||
if (!$stmt) {
|
||||
die("Fehler beim Prepare: " . $conn->error);
|
||||
}
|
||||
|
||||
if (!$stmt->execute($values)) {
|
||||
die("Fehler beim Execute: " . $stmt->error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getFKValue($conn, $table, $key, $value, $id, &$cache) {
|
||||
if ($id === null) return null;
|
||||
|
||||
$cacheKey = $table . "_" . $id;
|
||||
|
||||
if (isset($cache[$cacheKey])) {
|
||||
return $cache[$cacheKey];
|
||||
}
|
||||
|
||||
$stmt = $conn->prepare("SELECT `$value` FROM `$table` WHERE `$key` = ? LIMIT 1");
|
||||
if (!$stmt) {
|
||||
die("FK Prepare Fehler: " . $conn->error);
|
||||
}
|
||||
|
||||
$stmt->bind_param("i", $id);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->get_result()->fetch_assoc();
|
||||
$valueResult = $result[$value] ?? null;
|
||||
|
||||
$cache[$cacheKey] = $valueResult;
|
||||
|
||||
return $valueResult;
|
||||
}
|
||||
|
||||
function DatabaseShowWithFK($table, $fkMap = [], $columns = "*", $where = "", ...$values) {
|
||||
include_once 'DatabaseConnect.php';
|
||||
$conn = db_connect();
|
||||
|
||||
$cache = [];
|
||||
|
||||
$select = is_array($columns) ? implode(", ", $columns) : $columns;
|
||||
|
||||
$sql = "SELECT $select FROM `$table`";
|
||||
if (!empty($where)) {
|
||||
$sql .= " WHERE $where";
|
||||
}
|
||||
|
||||
$stmt = $conn->prepare($sql);
|
||||
if (!$stmt) die("Fehler beim Prepare: " . $conn->error);
|
||||
|
||||
if (!empty($values)) {
|
||||
$stmt->execute($values);
|
||||
} else {
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
$result = $stmt->get_result();
|
||||
if (!$result) die("Fehler beim get_result: " . $conn->error);
|
||||
|
||||
$rows = $result->fetch_all(MYSQLI_ASSOC);
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
foreach ($fkMap as $fkColumn => $fkInfo) {
|
||||
|
||||
if (!isset($row[$fkColumn]) || $row[$fkColumn] === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row[$fkColumn] = getFKValue(
|
||||
$conn,
|
||||
$fkInfo['table'],
|
||||
$fkInfo['key'],
|
||||
$fkInfo['value'],
|
||||
$row[$fkColumn],
|
||||
$cache
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
"rows" => $rows
|
||||
];
|
||||
}
|
||||
|
||||
function DatabaseShowAllSchemas() {
|
||||
include_once 'DatabaseConnect.php';
|
||||
$conn = db_connect();
|
||||
|
||||
$result = $conn->query("
|
||||
SELECT TABLE_NAME, COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
ORDER BY TABLE_NAME, ORDINAL_POSITION
|
||||
");
|
||||
|
||||
if (!$result) {
|
||||
die("Fehler beim Laden des Schemas: " . $conn->error);
|
||||
}
|
||||
|
||||
$schema = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$schema[$row['TABLE_NAME']][] = $row['COLUMN_NAME'];
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
$Berufsschulberichte = [
|
||||
"FACH_S1" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S1" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S1" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S2" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S2" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S2" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S3" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S3" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S3" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S4" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S4" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S4" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S5" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S5" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S5" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S6" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S6" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S6" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S7" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S7" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S7" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
|
||||
"FACH_S8" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"LEHRER_S8" => ["table" => "Lehrer", "key" => "ID", "value" => "NAME"],
|
||||
"RAUM_S8" => ["table" => "Raum", "key" => "ID", "value" => "Raum_NR"],
|
||||
];
|
||||
|
||||
$Stundenplan = [
|
||||
"MO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"DI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"MI" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"DO" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
"FR" => ["table" => "Fach", "key" => "ID", "value" => "Fach"],
|
||||
];
|
||||
|
||||
$Berechtigungen = [
|
||||
"BENUTZER_ID" => ["table" => "Benutzer", "key" => "ID", "value" => "NAME"],
|
||||
"SEITEN_ID" => ["table" => "Backend_Seiten", "key" => "ID", "value" => "DATEINAME"]
|
||||
];
|
||||
@@ -1,102 +1,102 @@
|
||||
<?php
|
||||
|
||||
|
||||
function logging() {
|
||||
include_once( __DIR__ . '/../classes/getData.php');
|
||||
include_once( __DIR__ . '/../database/databaseConnect.php');
|
||||
include_once( __DIR__ . '/../utils/databaseMethods.php');
|
||||
include_once( __DIR__ . '/../utils/mailer.php');
|
||||
|
||||
if ((new getSpecificData())->getLoggingStatus("GENERAL_BACKEND_LOGGING")[0]['WERT'] == 'ON') {
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||
|
||||
try {
|
||||
|
||||
$ClientIP = (new getSpecificData())->getClientIP();
|
||||
$UserAgent = (new getSpecificData())->getUserAgent();
|
||||
$RequestURL = (new getSpecificData())->getRequestURL();
|
||||
$USERCOOKIE = (new getSpecificData())->getCookie("USER");
|
||||
$SessionID = (new getSpecificData())->getSessionID();
|
||||
$REQUESTTIME = (new getSpecificData())->getRequestTime();
|
||||
$QUERY = (new getSpecificData())->getQuery();
|
||||
$LAND = (new getSpecificData())->getGeoIP($ClientIP);
|
||||
$Whitelisted = (new getSpecificData())->getWhitelistedIPs();
|
||||
$node = (new getSpecificData())->getServerNode((new getSpecificData())->getServerIP());
|
||||
$username = $_SESSION['username'] ?? 'Unbekannt';
|
||||
$website = (new getSpecificData())->getWebsite();
|
||||
|
||||
if ((new getSpecificData())->getLoggingStatus("DEV_BACKEND_LOGGING")[0]['WERT'] == 'OFF') {
|
||||
if (in_array($ClientIP, $Whitelisted)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$db_conn = db_connect();
|
||||
|
||||
$stmt = $db_conn->prepare("SELECT * FROM logs WHERE
|
||||
`CLIENT_IP` LIKE ? and
|
||||
`REQUEST_URL` = ? and
|
||||
`REQUEST_URL` <> '/favicon.ico'
|
||||
");
|
||||
|
||||
$stmt->bind_param("ss", $ClientIP, $RequestURL);
|
||||
$stmt->execute();
|
||||
$stmt->store_result();
|
||||
|
||||
if ($RequestURL !== '/favicon.ico') {
|
||||
if ($stmt->num_rows > 0) { //wenn mindestens eine zeile vorhanden ist für die client ip dann führe updates durch, wenn nicht mache neue zeile
|
||||
$updateCounter = $db_conn->prepare("
|
||||
UPDATE logs
|
||||
SET COUNTER = COUNTER + 1
|
||||
WHERE `CLIENT_IP` = ?
|
||||
AND `REQUEST_URL` = ?
|
||||
");
|
||||
$updateCounter->bind_param("ss", $ClientIP, $RequestURL);
|
||||
$updateCounter->execute();
|
||||
|
||||
$updateLastRequest = $db_conn->prepare("
|
||||
UPDATE logs
|
||||
SET `LAST_REQUEST` = ?
|
||||
WHERE `CLIENT_IP` = ?
|
||||
AND `REQUEST_URL` = ?
|
||||
");
|
||||
$updateLastRequest->bind_param("sss", $REQUESTTIME, $ClientIP, $RequestURL);
|
||||
$updateLastRequest->execute();
|
||||
|
||||
$updateCookieValue = $db_conn->prepare("
|
||||
UPDATE logs
|
||||
SET `USER_COOKIE_VALUE` = ?
|
||||
WHERE `CLIENT_IP` = ?
|
||||
AND `REQUEST_URL` = ?
|
||||
");
|
||||
$updateCookieValue->bind_param("sss", $USERCOOKIE, $ClientIP, $RequestURL);
|
||||
$updateCookieValue->execute();
|
||||
} else {
|
||||
DatabaseInsert(
|
||||
"logs",
|
||||
"CLIENT_IP, LAND, SERVER_IP, USER_AGENT, REQUEST_URL, USER_COOKIE_VALUE, USER_SESSION_VALUE, FIRST_REQUEST, QUERY, WEBSITE, USER",
|
||||
$ClientIP,
|
||||
$LAND,
|
||||
$node,
|
||||
$UserAgent,
|
||||
$RequestURL,
|
||||
$USERCOOKIE,
|
||||
$SessionID,
|
||||
$REQUESTTIME,
|
||||
$QUERY,
|
||||
$website,
|
||||
$username
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (mysqli_sql_exception $e) {
|
||||
errorMail(
|
||||
"Logging failed",
|
||||
$e->getMessage(),
|
||||
true,
|
||||
);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
|
||||
function logging() {
|
||||
include_once 'DataController.php';
|
||||
include_once 'DatabaseConnect.php';
|
||||
include_once 'DatabaseController.php';
|
||||
include_once __DIR__ . '/../modules/Mail.php';
|
||||
|
||||
if ((new getSpecificData())->getLoggingStatus("GENERAL_BACKEND_LOGGING")[0]['WERT'] == 'ON') {
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||
|
||||
try {
|
||||
|
||||
$ClientIP = (new getSpecificData())->getClientIP();
|
||||
$UserAgent = (new getSpecificData())->getUserAgent();
|
||||
$RequestURL = (new getSpecificData())->getRequestURL();
|
||||
$USERCOOKIE = (new getSpecificData())->getCookie("USER");
|
||||
$SessionID = (new getSpecificData())->getSessionID();
|
||||
$REQUESTTIME = (new getSpecificData())->getRequestTime();
|
||||
$QUERY = (new getSpecificData())->getQuery();
|
||||
$LAND = (new getSpecificData())->getGeoIP($ClientIP);
|
||||
$Whitelisted = (new getSpecificData())->getWhitelistedIPs();
|
||||
$node = (new getSpecificData())->getServerNode((new getSpecificData())->getServerIP());
|
||||
$username = $_SESSION['username'] ?? 'Unbekannt';
|
||||
$website = (new getSpecificData())->getWebsite();
|
||||
|
||||
if ((new getSpecificData())->getLoggingStatus("DEV_BACKEND_LOGGING")[0]['WERT'] == 'OFF') {
|
||||
if (in_array($ClientIP, $Whitelisted)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$db_conn = db_connect();
|
||||
|
||||
$stmt = $db_conn->prepare("SELECT * FROM logs WHERE
|
||||
`CLIENT_IP` LIKE ? and
|
||||
`REQUEST_URL` = ? and
|
||||
`REQUEST_URL` <> '/favicon.ico'
|
||||
");
|
||||
|
||||
$stmt->bind_param("ss", $ClientIP, $RequestURL);
|
||||
$stmt->execute();
|
||||
$stmt->store_result();
|
||||
|
||||
if ($RequestURL !== '/favicon.ico') {
|
||||
if ($stmt->num_rows > 0) { //wenn mindestens eine zeile vorhanden ist für die client ip dann führe updates durch, wenn nicht mache neue zeile
|
||||
$updateCounter = $db_conn->prepare("
|
||||
UPDATE logs
|
||||
SET COUNTER = COUNTER + 1
|
||||
WHERE `CLIENT_IP` = ?
|
||||
AND `REQUEST_URL` = ?
|
||||
");
|
||||
$updateCounter->bind_param("ss", $ClientIP, $RequestURL);
|
||||
$updateCounter->execute();
|
||||
|
||||
$updateLastRequest = $db_conn->prepare("
|
||||
UPDATE logs
|
||||
SET `LAST_REQUEST` = ?
|
||||
WHERE `CLIENT_IP` = ?
|
||||
AND `REQUEST_URL` = ?
|
||||
");
|
||||
$updateLastRequest->bind_param("sss", $REQUESTTIME, $ClientIP, $RequestURL);
|
||||
$updateLastRequest->execute();
|
||||
|
||||
$updateCookieValue = $db_conn->prepare("
|
||||
UPDATE logs
|
||||
SET `USER_COOKIE_VALUE` = ?
|
||||
WHERE `CLIENT_IP` = ?
|
||||
AND `REQUEST_URL` = ?
|
||||
");
|
||||
$updateCookieValue->bind_param("sss", $USERCOOKIE, $ClientIP, $RequestURL);
|
||||
$updateCookieValue->execute();
|
||||
} else {
|
||||
DatabaseInsert(
|
||||
"logs",
|
||||
"CLIENT_IP, LAND, SERVER_IP, USER_AGENT, REQUEST_URL, USER_COOKIE_VALUE, USER_SESSION_VALUE, FIRST_REQUEST, QUERY, WEBSITE, USER",
|
||||
$ClientIP,
|
||||
$LAND,
|
||||
$node,
|
||||
$UserAgent,
|
||||
$RequestURL,
|
||||
$USERCOOKIE,
|
||||
$SessionID,
|
||||
$REQUESTTIME,
|
||||
$QUERY,
|
||||
$website,
|
||||
$username
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (mysqli_sql_exception $e) {
|
||||
errorMail(
|
||||
"Logging failed",
|
||||
$e->getMessage(),
|
||||
true,
|
||||
);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
include_once(__DIR__ . '/DatabaseController.php');
|
||||
include_once(__DIR__ . '/DataController.php');
|
||||
|
||||
class Notification
|
||||
{
|
||||
protected $table;
|
||||
protected $seenTable;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->table = "Benachrichtigung";
|
||||
$this->seenTable = "Benachrichtigung_Gesehen";
|
||||
}
|
||||
|
||||
/**
|
||||
* Ermittelt die Benutzer-ID des aktuell eingeloggten Nutzers.
|
||||
*/
|
||||
private function getCurrentUserId(): ?int
|
||||
{
|
||||
$username = $_SESSION['username'] ?? null;
|
||||
if (!$username) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$rows = DatabaseShow(
|
||||
"Benutzer",
|
||||
"ID",
|
||||
"NAME = ?",
|
||||
$username
|
||||
);
|
||||
|
||||
return $rows[0]['ID'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alle Nachrichten (z.B. für eine Verwaltungsansicht).
|
||||
*/
|
||||
public function getAllMessages(): array
|
||||
{
|
||||
return DatabaseShow(
|
||||
$this->table,
|
||||
"ID, TITEL, NACHRICHT"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nur die Nachrichten, die der aktuelle Nutzer noch NICHT gesehen hat.
|
||||
* Genau das braucht das Modal, um zu entscheiden ob/was es anzeigt.
|
||||
*/
|
||||
public function getUnseenMessages(): array
|
||||
{
|
||||
$userId = $this->getCurrentUserId();
|
||||
if ($userId === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$alleNachrichten = DatabaseShow(
|
||||
$this->table,
|
||||
"ID, TITEL, NACHRICHT"
|
||||
);
|
||||
|
||||
$gesehenRows = DatabaseShow(
|
||||
$this->seenTable,
|
||||
"NACHRICHTEN_ID",
|
||||
"BENUTZER_ID = ?",
|
||||
$userId
|
||||
);
|
||||
|
||||
$gesehenIds = array_column($gesehenRows, 'NACHRICHTEN_ID');
|
||||
|
||||
return array_values(array_filter(
|
||||
$alleNachrichten,
|
||||
fn($nachricht) => !in_array($nachricht['ID'], $gesehenIds)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Legt eine neue Nachricht im Pool an (z.B. für eine Admin-Funktion).
|
||||
*/
|
||||
public function createMessage(string $titel, string $nachricht): bool
|
||||
{
|
||||
try {
|
||||
DatabaseInsert(
|
||||
$this->table,
|
||||
"TITEL, NACHRICHT",
|
||||
$titel,
|
||||
$nachricht
|
||||
);
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Markiert eine bestimmte Nachricht als vom aktuellen Nutzer gesehen/bestätigt.
|
||||
*/
|
||||
public function markAsSeen(int $nachrichtId): bool
|
||||
{
|
||||
$userId = $this->getCurrentUserId();
|
||||
if ($userId === null) {
|
||||
throw new Exception('Kein eingeloggter Benutzer gefunden.');
|
||||
}
|
||||
|
||||
// Prüfen, ob schon als gesehen markiert, um Duplikate zu vermeiden
|
||||
$bereitsGesehen = DatabaseShow(
|
||||
$this->seenTable,
|
||||
"ID",
|
||||
"BENUTZER_ID = ? AND NACHRICHTEN_ID = ?",
|
||||
$userId,
|
||||
$nachrichtId
|
||||
);
|
||||
|
||||
if (!empty($bereitsGesehen)) {
|
||||
return true; // war schon markiert, kein Fehler
|
||||
}
|
||||
|
||||
try {
|
||||
DatabaseInsert(
|
||||
$this->seenTable,
|
||||
"BENUTZER_ID, NACHRICHTEN_ID",
|
||||
$userId,
|
||||
$nachrichtId
|
||||
);
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function updateMessage(int $id, string $titel, string $nachricht): bool
|
||||
{
|
||||
try {
|
||||
DatabaseUpdate(
|
||||
$this->table,
|
||||
"TITEL = ?, NACHRICHT = ?",
|
||||
"ID = ?",
|
||||
$titel,
|
||||
$nachricht,
|
||||
$id
|
||||
);
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht eine Nachricht komplett, inkl. aller "Gesehen"-Einträge dazu.
|
||||
*/
|
||||
public function deleteMessage(int $id): bool
|
||||
{
|
||||
try {
|
||||
DatabaseDelete($this->seenTable, "NACHRICHTEN_ID = ?", $id);
|
||||
DatabaseDelete($this->table, "ID = ?", $id);
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Anzahl Nutzer, die eine bestimmte Nachricht bereits bestätigt haben.
|
||||
*/
|
||||
public function countSeenBy(int $id): int
|
||||
{
|
||||
$rows = DatabaseShow(
|
||||
$this->seenTable,
|
||||
"ID",
|
||||
"NACHRICHTEN_ID = ?",
|
||||
$id
|
||||
);
|
||||
return count($rows);
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -1,10 +1,9 @@
|
||||
<?php
|
||||
|
||||
include_once(__DIR__ . '/../utils/formatter.php');
|
||||
require_once(__DIR__ . '/DatabaseController.php');
|
||||
|
||||
class RightsController {
|
||||
|
||||
use ReponseFormatter;
|
||||
/**
|
||||
* save new site with sitename
|
||||
*
|
||||
@@ -62,8 +61,6 @@ class RightsController {
|
||||
*/
|
||||
public function RightsHandler($sitename, $name = 'Bruno', $lastname = 'Eckhardt') {
|
||||
try {
|
||||
$sitename = strtolower($sitename);
|
||||
$this->saveSite($sitename);
|
||||
|
||||
DatabaseInsert(
|
||||
"Benutzer_Berechtigungen",
|
||||
@@ -1,30 +1,30 @@
|
||||
<?php
|
||||
|
||||
function redirectToWebsite(string $website) {
|
||||
$website = trim($website);
|
||||
|
||||
if ($website === '') {
|
||||
die("Ungültige URL.");
|
||||
}
|
||||
|
||||
$blocked_schemes = ['javascript:', 'data:', 'file:'];
|
||||
foreach ($blocked_schemes as $scheme) {
|
||||
if (stripos($website, $scheme) === 0) {
|
||||
die("Ungültige URL.");
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/^https?:\/\//i', $website)) {
|
||||
header("Location: " . $website);
|
||||
exit;
|
||||
}
|
||||
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https://" : "http://";
|
||||
$domain = $_SERVER['HTTP_HOST'];
|
||||
|
||||
$website = ltrim($website, "/");
|
||||
$redirectUrl = $protocol . $domain . "/" . $website;
|
||||
|
||||
header("Location: " . $redirectUrl);
|
||||
exit;
|
||||
<?php
|
||||
function redirectToWebsite(string $website): void
|
||||
{
|
||||
$website = trim($website);
|
||||
|
||||
if ($website === '') {
|
||||
die("Ungültige URL.");
|
||||
}
|
||||
|
||||
$blocked_schemes = ['javascript:', 'data:', 'file:'];
|
||||
foreach ($blocked_schemes as $scheme) {
|
||||
if (stripos($website, $scheme) === 0) {
|
||||
die("Ungültige URL.");
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/^https?:\/\//i', $website)) {
|
||||
header("Location: " . $website);
|
||||
exit;
|
||||
}
|
||||
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https://" : "http://";
|
||||
$domain = $_SERVER['HTTP_HOST'];
|
||||
|
||||
$website = ltrim($website, "/");
|
||||
$redirectUrl = $protocol . $domain . "/" . $website;
|
||||
|
||||
header("Location: " . $redirectUrl);
|
||||
exit;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
|
||||
class UserController {
|
||||
public function setUser($name, $nachname, $email, $passwort, $rolle, $admin, $home_ip) {
|
||||
|
||||
@@ -24,13 +23,6 @@ class UserController {
|
||||
"NAME = ?",
|
||||
$name
|
||||
);
|
||||
|
||||
DatabaseInsert(
|
||||
"Benutzer_Berechtigungen",
|
||||
"BENUTZER_ID, SEITEN_ID",
|
||||
$userID[0]['ID'],
|
||||
"8"
|
||||
);
|
||||
}
|
||||
|
||||
public function DeleteUser($name, $nachname, $email) {
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/controller/RoutesController.php');
|
||||
include_once(__DIR__ . '/controller/DataController.php');
|
||||
include_once(__DIR__ . '/modules/Mail.php');
|
||||
|
||||
$message = null;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
|
||||
if ($email !== '') {
|
||||
$benutzerRows = DatabaseShow("Benutzer", "ID, NAME", "EMAIL = ?", $email);
|
||||
|
||||
if (!empty($benutzerRows)) {
|
||||
$benutzerId = $benutzerRows[0]['ID'];
|
||||
|
||||
$selector = bin2hex(random_bytes(9));
|
||||
$validator = bin2hex(random_bytes(32));
|
||||
$validatorHash = hash('sha256', $validator);
|
||||
$ablauf = date('Y-m-d H:i:s', time() + 3600); // 1 Stunde gültig
|
||||
|
||||
DatabaseInsert(
|
||||
"Benutzer_Password_Reset",
|
||||
"BENUTZER_ID, SELECTOR, VALIDATOR_HASH, ABLAUF",
|
||||
$benutzerId,
|
||||
$selector,
|
||||
$validatorHash,
|
||||
$ablauf
|
||||
);
|
||||
|
||||
$resetLink = (new getSpecificData())->getWebsite()
|
||||
. "/reset_password.php?selector=$selector&validator=$validator";
|
||||
|
||||
sendMail(
|
||||
"Passwort zurücksetzen",
|
||||
"Klicke auf folgenden Link, um dein Passwort zurückzusetzen (gültig 1 Stunde):\n$resetLink",
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true,
|
||||
$email // WICHTIG: an die Nutzer-Email senden, nicht an die Standard-Adresse!
|
||||
);
|
||||
}
|
||||
// Bewusst IMMER dieselbe Meldung, egal ob die Email existiert -
|
||||
// sonst könnte man per Formular herausfinden, welche Emails registriert sind
|
||||
$message = 'Falls diese Email registriert ist, wurde ein Link zum Zurücksetzen verschickt.';
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Passwort vergessen</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<h2>Passwort vergessen</h2>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<p class="login-error" style="color:#28a745;"><?= htmlspecialchars($message) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" class="register-form">
|
||||
<input type="email" name="email" placeholder="Deine Email" required>
|
||||
<button type="submit">Link anfordern</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+114
-129
@@ -8,28 +8,22 @@ session_set_cookie_params([
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax'
|
||||
]);
|
||||
|
||||
session_start();
|
||||
|
||||
if (empty($_SESSION['user_logged_in'])) {
|
||||
header('Location: login.php');
|
||||
header('Location: login');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/src/MVC/autoloader.php';
|
||||
|
||||
include_once( __DIR__ . '/config.php');
|
||||
include_once( __DIR__ . '/src/classes/getData.php');
|
||||
include_once( __DIR__ . '/src/controller/apiController.php');
|
||||
include_once( __DIR__ . '/src/controller/cookieController.php');
|
||||
include_once( __DIR__ . '/src/controller/rightsController.php');
|
||||
include_once( __DIR__ . '/src/controller/userController.php');
|
||||
include_once( __DIR__ . '/src/database/databaseConnect.php');
|
||||
include_once( __DIR__ . '/src/utils/Debugging/ConsoleLogger.php');
|
||||
include_once( __DIR__ . '/src/utils/databaseMethods.php');
|
||||
include_once( __DIR__ . '/src/utils/formatter.php');
|
||||
include_once( __DIR__ . '/src/utils/mailer.php');
|
||||
include_once( __DIR__ . '/src/utils/requestLogger.php');
|
||||
include_once( __DIR__ . '/src/utils/router.php');
|
||||
include_once 'config.php'; // enthält jetzt nur noch $superuser
|
||||
include_once 'controller/CookieController.php';
|
||||
include_once 'controller/DatabaseConnect.php';
|
||||
include_once 'controller/DatabaseController.php';
|
||||
include_once 'controller/DataController.php';
|
||||
include_once 'controller/LogController.php';
|
||||
include_once 'controller/UserController.php';
|
||||
include_once 'modules/ConsoleLog.php';
|
||||
|
||||
$user_id = DatabaseShow(
|
||||
"Benutzer",
|
||||
@@ -38,6 +32,7 @@ $user_id = DatabaseShow(
|
||||
$_SESSION['username'],
|
||||
);
|
||||
|
||||
// Berechtigte Seiten-IDs des Nutzers (bleibt unverändert)
|
||||
$rightsArray = DatabaseShowWithFK(
|
||||
"Benutzer_Berechtigungen",
|
||||
$Berechtigungen,
|
||||
@@ -45,9 +40,47 @@ $rightsArray = DatabaseShowWithFK(
|
||||
"BENUTZER_ID = ?",
|
||||
$user_id[0]['ID'],
|
||||
);
|
||||
|
||||
$userRights = array_map('trim', array_column($rightsArray['rows'], 'SEITEN_ID'));
|
||||
|
||||
$isAdmin = isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true;
|
||||
// --- Seiten & Gruppen aus der DB laden ---
|
||||
$seitenGruppenRows = DatabaseShow(
|
||||
"Backend_Seiten_Gruppen",
|
||||
"ID, LABEL, DEFAULT_OPEN, SORTIERUNG"
|
||||
);
|
||||
usort($seitenGruppenRows, fn($a, $b) => ($a['SORTIERUNG'] ?? 0) <=> ($b['SORTIERUNG'] ?? 0));
|
||||
|
||||
$seitenRows = DatabaseShow(
|
||||
"Backend_Seiten",
|
||||
"ID, DATEINAME, NAME, AKTIV, ADMIN_ONLY, GRUPPE_ID, SORTIERUNG"
|
||||
);
|
||||
usort($seitenRows, fn($a, $b) => ($a['SORTIERUNG'] ?? 0) <=> ($b['SORTIERUNG'] ?? 0));
|
||||
|
||||
$services = [];
|
||||
foreach ($seitenRows as $row) {
|
||||
$services[$row['DATEINAME']] = [
|
||||
'id' => $row['ID'],
|
||||
'name' => $row['NAME'],
|
||||
'active' => (bool)$row['AKTIV'],
|
||||
'adminOnly' => (bool)$row['ADMIN_ONLY'],
|
||||
'gruppeId' => $row['GRUPPE_ID'],
|
||||
];
|
||||
}
|
||||
|
||||
$serviceGroups = [];
|
||||
foreach ($seitenGruppenRows as $gruppe) {
|
||||
$servicesInGruppe = array_keys(array_filter(
|
||||
$services,
|
||||
fn($s) => $s['gruppeId'] == $gruppe['ID']
|
||||
));
|
||||
$serviceGroups[$gruppe['LABEL']] = [
|
||||
'label' => $gruppe['LABEL'],
|
||||
'defaultOpen' => (bool)$gruppe['DEFAULT_OPEN'],
|
||||
'services' => $servicesInGruppe,
|
||||
];
|
||||
}
|
||||
|
||||
// --- Selected Service Logik bleibt unverändert ---
|
||||
$selectedService = null;
|
||||
|
||||
if (
|
||||
@@ -58,142 +91,94 @@ if (
|
||||
$selectedService = $_GET['service'];
|
||||
}
|
||||
|
||||
if (!$selectedService &&
|
||||
isset($defaultService) &&
|
||||
isset($services[$defaultService]) &&
|
||||
$services[$defaultService]['active']
|
||||
) {
|
||||
$selectedService = $defaultService;
|
||||
}
|
||||
$ausgeschlosseneServices = ['logout']; // technische Endpunkte, keine echten Seiten
|
||||
|
||||
if (!$selectedService) {
|
||||
foreach ($services as $key => $service) {
|
||||
if (in_array($key, $ausgeschlosseneServices, true)) continue;
|
||||
if ($service['active']) {
|
||||
$selectedService = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($selectedService && !in_array($selectedService, $userRights, true)) {
|
||||
$selectedService = null;
|
||||
}
|
||||
|
||||
$isAdmin = isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true;
|
||||
|
||||
if ((new getSpecificData())->getWebsite() != "backend.blackpi.ipv64.net") {
|
||||
if ((new getSpecificData())->getWebsite() != "backend.mrhossa.de") {
|
||||
$title = "DEV Backend";
|
||||
} else {
|
||||
$title = "Backend";
|
||||
}
|
||||
|
||||
// MVC Logic
|
||||
try {
|
||||
$router = new \MVC\Core\Router();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
// Register Routes
|
||||
$router->add('home_info', \MVC\Controller\ServiceController::class, 'home_info', ['auth']);
|
||||
$router->add('links', \MVC\Controller\ServiceController::class, 'links', ['auth']);
|
||||
$router->add('documentation', \MVC\Controller\DocumentationController::class, 'links', ['auth']);
|
||||
$router->add('logout', \MVC\Controller\ServiceController::class, 'logout', ['auth']);
|
||||
|
||||
$router->add('public_request_logs', \MVC\Controller\LogController::class, 'public_request_logs', ['auth']);
|
||||
$router->add('backend_request_logs', \MVC\Controller\LogController::class, 'backend_request_logs', ['auth']);
|
||||
$router->add('personal_request_logs', \MVC\Controller\LogController::class, 'personal_request_logs', ['auth', 'admin']);
|
||||
$router->add('public_request_chart', \MVC\Controller\LogController::class, 'public_request_chart', ['auth']);
|
||||
$router->add('backend_request_chart', \MVC\Controller\LogController::class, 'backend_request_chart', ['auth']);
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
$router->add('user_control', \MVC\Controller\AdminController::class, 'user_control', ['auth', 'admin']);
|
||||
$router->add('services_control', \MVC\Controller\AdminController::class, 'services_control', ['auth', 'admin']);
|
||||
$router->add('variablen_control', \MVC\Controller\AdminController::class, 'variablen_control', ['auth', 'admin']);
|
||||
$router->add('data_info', \MVC\Controller\AdminController::class, 'data_info', ['auth', 'admin']);
|
||||
$router->add('tabellen', \MVC\Controller\AdminController::class, 'tabellen', ['auth', 'admin']);
|
||||
<body>
|
||||
<header class="main-header">
|
||||
Backend
|
||||
<button id="darkModeToggle">Dark Mode</button>
|
||||
<button id="logoutButton">Logout</button>
|
||||
</header>
|
||||
<div class="main">
|
||||
<div class="sidebar">
|
||||
<?php foreach ($serviceGroups as $groupKey => $group): ?>
|
||||
<?php
|
||||
$visibleServices = [];
|
||||
|
||||
$router->add('berichte', \MVC\Controller\ReportController::class, 'berichte', ['auth']);
|
||||
$router->add('berichte_arbeit_gen', \MVC\Controller\ReportController::class, 'berichte_arbeit_gen', ['auth']);
|
||||
$router->add('zeiterfassung', \MVC\Controller\ReportController::class, 'zeiterfassung', ['auth']);
|
||||
$router->add('print_static', \MVC\Controller\ReportController::class, 'print_static', ['auth']);
|
||||
foreach ($group['services'] as $serviceKey) {
|
||||
if (!isset($services[$serviceKey])) continue;
|
||||
if (!$services[$serviceKey]['active']) continue;
|
||||
if (!empty($services[$serviceKey]['adminOnly']) && !$isAdmin) continue;
|
||||
if (!in_array($serviceKey, $userRights, true)) continue;
|
||||
$visibleServices[] = $serviceKey;
|
||||
}
|
||||
|
||||
$router->add('api_tester', \MVC\Controller\ApiController::class, 'api_tester', ['auth', 'admin']);
|
||||
$router->add('weather_chart', \MVC\Controller\StatsController::class, 'weather_chart', ['auth', 'admin']);
|
||||
$router->add('domains', \MVC\Controller\DomainController::class, 'domains', ['auth']);
|
||||
$router->add('documentation', \MVC\Controller\DocumentationController::class, 'index', ['auth']);
|
||||
if (empty($visibleServices)) continue;
|
||||
|
||||
$layout = new \MVC\Layout\Layout('main');
|
||||
$layout->setBlock('title', $title);
|
||||
$isOpen = !empty($group['defaultOpen']) || in_array($selectedService, $visibleServices, true);
|
||||
?>
|
||||
<div class="service-group <?= $isOpen ? 'open' : '' ?>" data-group="<?= htmlspecialchars($groupKey) ?>">
|
||||
|
||||
// Header Block
|
||||
ob_start();
|
||||
?>
|
||||
Backend
|
||||
<button id="darkModeToggle">Dark Mode</button>
|
||||
<button id="logoutButton">Logout</button>
|
||||
<?php
|
||||
$layout->setBlock('header', ob_get_clean());
|
||||
<div class="group-title">
|
||||
<?= htmlspecialchars($group['label']) ?>
|
||||
<span class="arrow">▸</span>
|
||||
</div>
|
||||
|
||||
// Sidebar Block
|
||||
ob_start();
|
||||
?>
|
||||
<?php foreach ($serviceGroups as $groupKey => $group): ?>
|
||||
<?php
|
||||
$visibleServices = [];
|
||||
|
||||
foreach ($group['services'] as $serviceKey) {
|
||||
if (!isset($services[$serviceKey])) continue;
|
||||
if (!$services[$serviceKey]['active']) continue;
|
||||
if (!empty($services[$serviceKey]['adminOnly']) && !$isAdmin) continue;
|
||||
if (!in_array($serviceKey, $userRights, true)) continue;
|
||||
$visibleServices[] = $serviceKey;
|
||||
}
|
||||
|
||||
if (empty($visibleServices)) continue;
|
||||
|
||||
$isOpen = !empty($group['defaultOpen']) || in_array($selectedService, $visibleServices, true);
|
||||
?>
|
||||
<div class="service-group <?= $isOpen ? 'open' : '' ?>" data-group="<?= $groupKey ?>">
|
||||
|
||||
<div class="group-title">
|
||||
<?= htmlspecialchars($group['label']) ?>
|
||||
<span class="arrow">▸</span>
|
||||
<div class="group-services">
|
||||
<?php foreach ($visibleServices as $serviceKey): ?>
|
||||
<a href="?service=<?= $serviceKey ?>">
|
||||
<button class="<?= $selectedService === $serviceKey ? 'active' : '' ?>">
|
||||
<?= htmlspecialchars($services[$serviceKey]['name']) ?>
|
||||
</button>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="content">
|
||||
<?php
|
||||
if ($selectedService) {
|
||||
include "services/$selectedService.php";
|
||||
logging();
|
||||
} else {
|
||||
echo "<p>Kein aktiver Service verfügbar.</p>";
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
|
||||
<div class="group-services">
|
||||
<?php foreach ($visibleServices as $serviceKey): ?>
|
||||
<a href="?service=<?= $serviceKey ?>">
|
||||
<button class="<?= $selectedService === $serviceKey ? 'active' : '' ?>">
|
||||
<?= htmlspecialchars($services[$serviceKey]['name']) ?>
|
||||
</button>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php
|
||||
$layout->setBlock('sidebar', ob_get_clean());
|
||||
|
||||
// Content Block
|
||||
if ($selectedService) {
|
||||
$content = $router->dispatch($selectedService);
|
||||
$layout->setBlock('content', $content);
|
||||
logging();
|
||||
} else {
|
||||
$layout->setBlock('content', "<p>Kein aktiver Service verfügbar.</p>");
|
||||
}
|
||||
|
||||
echo $layout->render();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Central Error Handling
|
||||
error_log($e->getMessage());
|
||||
http_response_code(500);
|
||||
|
||||
if ($isAdmin) {
|
||||
echo "<h1>Internal Server Error</h1>";
|
||||
echo "<p><strong>Message:</strong> " . $e->getMessage() . "</p>";
|
||||
echo "<p><strong>File:</strong> " . $e->getFile() . " on line " . $e->getLine() . "</p>";
|
||||
echo "<pre>" . $e->getTraceAsString() . "</pre>";
|
||||
} else {
|
||||
echo "<h1>Hoppla!</h1>";
|
||||
echo "<p>Ein interner Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.</p>";
|
||||
}
|
||||
}
|
||||
</html>
|
||||
@@ -0,0 +1,72 @@
|
||||
const AJAX_HANDLER_URL = '/api/js/AjaxHandler';
|
||||
|
||||
function escapeHtml(str) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = str ?? '';
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
async function parseJsonResponse(response) {
|
||||
const rawText = await response.text();
|
||||
try {
|
||||
return JSON.parse(rawText);
|
||||
} catch (e) {
|
||||
console.error('Antwort war kein gültiges JSON:', rawText);
|
||||
throw new Error('Server hat kein gültiges JSON zurückgegeben (siehe Konsole)');
|
||||
}
|
||||
}
|
||||
|
||||
async function ajaxRequest(payload) {
|
||||
try {
|
||||
const response = await fetch(AJAX_HANDLER_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const data = await parseJsonResponse(response);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ajax-Fehler:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function DatabaseInsert(table, columns, values) {
|
||||
return ajaxRequest({
|
||||
action: 'insert',
|
||||
table,
|
||||
columns,
|
||||
values
|
||||
});
|
||||
}
|
||||
|
||||
function DatabaseShow(table, columns = [], conditions = []) {
|
||||
return ajaxRequest({
|
||||
action: 'show',
|
||||
table,
|
||||
columns,
|
||||
conditions
|
||||
});
|
||||
}
|
||||
|
||||
function DatabaseUpdate(table, updates, conditions) {
|
||||
return ajaxRequest({
|
||||
action: 'update',
|
||||
table,
|
||||
updates,
|
||||
conditions
|
||||
});
|
||||
}
|
||||
|
||||
function DatabaseDelete(table, conditions) {
|
||||
return ajaxRequest({
|
||||
action: 'delete',
|
||||
table,
|
||||
conditions
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
|
||||
function renderTable(wrapperEl, entries, spaltenKonfig, idSpalte, onDelete) {
|
||||
|
||||
if (entries && !Array.isArray(entries)) {
|
||||
console.warn('entries war kein Array:', entries);
|
||||
entries = Object.values(entries);
|
||||
}
|
||||
|
||||
if (!entries || entries.length === 0) {
|
||||
wrapperEl.innerHTML = '<p>Keine Einträge vorhanden.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
${spaltenKonfig.map(s => `<th>${escapeHtml(s.label)}</th>`).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
for (const entry of entries) {
|
||||
html += `<tr>`;
|
||||
for (const s of spaltenKonfig) {
|
||||
html += `<td data-label="${escapeHtml(s.label)}">${escapeHtml(entry[s.key])}</td>`;
|
||||
}
|
||||
}
|
||||
|
||||
html += `</tbody>
|
||||
</table>`;
|
||||
wrapperEl.innerHTML = html;
|
||||
|
||||
wrapperEl.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du diesen Eintrag wirklich löschen?')) return;
|
||||
await onDelete(btn.dataset.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function renderTableWithDelete(wrapperEl, entries, spaltenKonfig, idSpalte, onDelete) {
|
||||
|
||||
if (entries && !Array.isArray(entries)) {
|
||||
console.warn('entries war kein Array:', entries);
|
||||
entries = Object.values(entries);
|
||||
}
|
||||
|
||||
if (!entries || entries.length === 0) {
|
||||
wrapperEl.innerHTML = '<p>Keine Einträge vorhanden.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
${spaltenKonfig.map(s => `<th>${escapeHtml(s.label)}</th>`).join('')}
|
||||
<th style="width: 100px;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
for (const entry of entries) {
|
||||
html += `<tr>`;
|
||||
for (const s of spaltenKonfig) {
|
||||
html += `<td data-label="${escapeHtml(s.label)}">${escapeHtml(entry[s.key])}</td>`;
|
||||
}
|
||||
html += `
|
||||
<td data-label="Aktion">
|
||||
<button type="button" class="btn-delete" data-id="${escapeHtml(entry[idSpalte])}">
|
||||
Löschen
|
||||
</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
html += `</tbody>
|
||||
</table>`;
|
||||
wrapperEl.innerHTML = html;
|
||||
|
||||
wrapperEl.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du diesen Eintrag wirklich löschen?')) return;
|
||||
await onDelete(btn.dataset.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// [Erweiterung für /js/AjaxMethods.js]
|
||||
function renderTableWithEdit(wrapperEl, entries, spaltenKonfig, idSpalte, onEdit, onDelete) {
|
||||
|
||||
if (entries && !Array.isArray(entries)) {
|
||||
console.warn('entries war kein Array:', entries);
|
||||
entries = Object.values(entries);
|
||||
}
|
||||
|
||||
if (!entries || entries.length === 0) {
|
||||
wrapperEl.innerHTML = '<p>Keine Einträge vorhanden.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
${spaltenKonfig.map(s => `<th>${escapeHtml(s.label)}</th>`).join('')}
|
||||
<th style="width: 180px;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
for (const entry of entries) {
|
||||
html += `<tr>`;
|
||||
for (const s of spaltenKonfig) {
|
||||
html += `<td data-label="${escapeHtml(s.label)}">${escapeHtml(entry[s.key])}</td>`;
|
||||
}
|
||||
html += `
|
||||
<td data-label="Aktion">
|
||||
<button type="button" class="btn-edit" data-id="${escapeHtml(entry[idSpalte])}" style="margin-right: 5px; padding: 6px 12px;">
|
||||
Bearbeiten
|
||||
</button>
|
||||
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
html += `</tbody>
|
||||
</table>`;
|
||||
wrapperEl.innerHTML = html;
|
||||
|
||||
// Event-Listener für Edit
|
||||
wrapperEl.querySelectorAll('.btn-edit').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
await onEdit(btn.dataset.id);
|
||||
});
|
||||
});
|
||||
|
||||
// Event-Listener für Löschen
|
||||
wrapperEl.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du diesen Eintrag wirklich löschen?')) return;
|
||||
await onDelete(btn.dataset.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function renderTableWithEditAndDelete(wrapperEl, entries, spaltenKonfig, idSpalte, onEdit, onDelete) {
|
||||
|
||||
if (entries && !Array.isArray(entries)) {
|
||||
console.warn('entries war kein Array:', entries);
|
||||
entries = Object.values(entries);
|
||||
}
|
||||
|
||||
if (!entries || entries.length === 0) {
|
||||
wrapperEl.innerHTML = '<p>Keine Einträge vorhanden.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
${spaltenKonfig.map(s => `<th>${escapeHtml(s.label)}</th>`).join('')}
|
||||
<th style="width: 180px;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
for (const entry of entries) {
|
||||
html += `<tr>`;
|
||||
for (const s of spaltenKonfig) {
|
||||
html += `<td data-label="${escapeHtml(s.label)}">${escapeHtml(entry[s.key])}</td>`;
|
||||
}
|
||||
html += `
|
||||
<td data-label="Aktion">
|
||||
<button type="button" class="btn-edit" data-id="${escapeHtml(entry[idSpalte])}" style="margin-right: 5px; padding: 6px 12px;">
|
||||
Bearbeiten
|
||||
</button>
|
||||
<button type="button" class="btn-delete" data-id="${escapeHtml(entry[idSpalte])}" style="margin-right: 5px; padding: 6px 12px;">
|
||||
Löschen
|
||||
</button>
|
||||
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
html += `</tbody>
|
||||
</table>`;
|
||||
wrapperEl.innerHTML = html;
|
||||
|
||||
// Event-Listener für Edit
|
||||
wrapperEl.querySelectorAll('.btn-edit').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
await onEdit(btn.dataset.id);
|
||||
});
|
||||
});
|
||||
|
||||
// Event-Listener für Löschen
|
||||
wrapperEl.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du diesen Eintrag wirklich löschen?')) return;
|
||||
await onDelete(btn.dataset.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
const BENACHRICHTIGUNGEN_HANDLER_URL = '/api/js/handler/BenachrichtigungenAjaxHandler';
|
||||
|
||||
async function benachrichtigungenAjaxRequest(payload) {
|
||||
try {
|
||||
const response = await fetch(BENACHRICHTIGUNGEN_HANDLER_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const data = await parseJsonResponse(response);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Benachrichtigungen-Ajax-Fehler:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function getNotificationEntries() {
|
||||
return benachrichtigungenAjaxRequest({ action: 'get_entries' });
|
||||
}
|
||||
|
||||
function addNotificationEntry(titel, nachricht) {
|
||||
return benachrichtigungenAjaxRequest({ action: 'add_entry', titel, nachricht });
|
||||
}
|
||||
|
||||
function editNotificationEntry(id, titel, nachricht) {
|
||||
return benachrichtigungenAjaxRequest({ action: 'edit_entry', id, titel, nachricht });
|
||||
}
|
||||
|
||||
function deleteNotificationEntry(id) {
|
||||
return benachrichtigungenAjaxRequest({ action: 'delete_entry', id });
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
const NOTIFICATION_HANDLER_URL = '/api/js/handler/NotificationAjaxHandler';
|
||||
|
||||
async function notificationAjaxRequest(payload) {
|
||||
try {
|
||||
const response = await fetch(NOTIFICATION_HANDLER_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const data = await parseJsonResponse(response);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Notification-Ajax-Fehler:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function markNotificationAsSeen(nachrichtId) {
|
||||
return notificationAjaxRequest({ action: 'mark_seen', nachricht_id: nachrichtId });
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
const TERMINE_HANDLER_URL = '/api/js/handler/TermineAjaxHandler';
|
||||
|
||||
async function termineAjaxRequest(payload) {
|
||||
try {
|
||||
const response = await fetch(TERMINE_HANDLER_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const data = await parseJsonResponse(response);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Termine-Ajax-Fehler:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function getTermine() {
|
||||
return termineAjaxRequest({ action: 'get_entries' });
|
||||
}
|
||||
|
||||
function addTermin(datum, zeit, name, notiz, wiederholung, enddatum) {
|
||||
return termineAjaxRequest({ action: 'add_termin', datum, zeit, name, notiz, wiederholung, enddatum });
|
||||
}
|
||||
|
||||
function deleteTermin(id) {
|
||||
return termineAjaxRequest({ action: 'delete_termin', id });
|
||||
}
|
||||
|
||||
function deleteTerminSerie(serienId) {
|
||||
return termineAjaxRequest({ action: 'delete_serie', serienId });
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
const TICKETS_HANDLER_URL = '/api/js/handler/TicketBoardAjaxHandler';
|
||||
|
||||
async function ticketsAjaxRequest(payload) {
|
||||
try {
|
||||
const response = await fetch(TICKETS_HANDLER_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const data = await parseJsonResponse(response);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Tickets-Ajax-Fehler:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function getTicketEntries() {
|
||||
return ticketsAjaxRequest({ action: 'get_entries' });
|
||||
}
|
||||
|
||||
function addTicketEntry(titel, beschreibung) {
|
||||
return ticketsAjaxRequest({ action: 'add_entry', titel, beschreibung });
|
||||
}
|
||||
|
||||
function deleteTicketEntry(id) {
|
||||
return ticketsAjaxRequest({ action: 'delete_entry', id });
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
const TICKET_CONTRO_HANDLER_URL = '/api/js/handler/TicketControlAjaxHandler';
|
||||
|
||||
async function ticketControAjaxRequest(payload) {
|
||||
try {
|
||||
const response = await fetch(TICKET_CONTRO_HANDLER_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const data = await parseJsonResponse(response);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('TicketContro-Ajax-Fehler:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function getTicketControEntries() {
|
||||
return ticketControAjaxRequest({ action: 'get_entries' });
|
||||
}
|
||||
|
||||
function editTicketControEntry(id, status) {
|
||||
return ticketControAjaxRequest({ action: 'edit_entry', id, status });
|
||||
}
|
||||
|
||||
function deleteTicketControEntry(id) {
|
||||
return ticketControAjaxRequest({ action: 'delete_entry', id });
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
const VERGABE_HANDLER_URL = '/api/js/handler/VergabeAjaxHandler';
|
||||
|
||||
async function vergabeAjaxRequest(payload) {
|
||||
try {
|
||||
const response = await fetch(VERGABE_HANDLER_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const data = await parseJsonResponse(response);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Vergabe-Ajax-Fehler:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function getVergabeEntries(searchWer = '', searchWas = '') {
|
||||
return vergabeAjaxRequest({ action: 'get_entries', searchWer, searchWas });
|
||||
}
|
||||
|
||||
function getVergabeSuggestions() {
|
||||
return vergabeAjaxRequest({ action: 'get_suggestions' });
|
||||
}
|
||||
|
||||
function addVergabeEntry(was, wer, wann, rueckgabe) {
|
||||
return vergabeAjaxRequest({ action: 'add_entry', was, wer, wann, rueckgabe });
|
||||
}
|
||||
|
||||
function returnVergabeEntry(id) {
|
||||
return vergabeAjaxRequest({ action: 'return_entry', id });
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
const OVERTIME_HANDLER_URL = '/api/js/handler/ZeiterfassungAjaxHandler';
|
||||
|
||||
async function overtimeAjaxRequest(payload) {
|
||||
try {
|
||||
const response = await fetch(OVERTIME_HANDLER_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const data = await parseJsonResponse(response);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Overtime-Ajax-Fehler:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function getOvertimeStats() {
|
||||
return overtimeAjaxRequest({ action: 'get_stats' });
|
||||
}
|
||||
|
||||
function addOvertimeEntry(datum, minuten, grund) {
|
||||
return overtimeAjaxRequest({ action: 'add_overtime', datum, minuten, grund });
|
||||
}
|
||||
+70
-38
@@ -1,15 +1,17 @@
|
||||
<?php
|
||||
include_once 'controller/DatabaseController.php';
|
||||
include_once 'controller/RoutesController.php';
|
||||
include_once 'controller/LogController.php';
|
||||
include_once 'modules/Mail.php';
|
||||
include_once 'config.php';
|
||||
|
||||
include_once( __DIR__ . '/config.php');
|
||||
include_once( __DIR__ . '/src/utils/databaseMethods.php');
|
||||
include_once( __DIR__ . '/src/utils/requestLogger.php');
|
||||
include_once( __DIR__ . '/src/utils/mailer.php');
|
||||
ob_start();
|
||||
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => '',
|
||||
'secure' => false,
|
||||
'secure' => false,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax'
|
||||
]);
|
||||
@@ -19,6 +21,26 @@ logging();
|
||||
|
||||
$error = null;
|
||||
|
||||
$clientip = (new getSpecificData())->getClientIP();
|
||||
$useragent = (new getSpecificData())->getUserAgent();
|
||||
$referrer = (new getSpecificData())->getReferer();
|
||||
|
||||
function schreibeSessionUndPruefe(): void
|
||||
{
|
||||
$sid = session_id();
|
||||
$writeOk = session_write_close();
|
||||
$sessionFile = session_save_path() . '/sess_' . $sid;
|
||||
$content = @file_get_contents($sessionFile);
|
||||
|
||||
error_log(sprintf(
|
||||
"[LOGIN-SESSION-CHECK] SID=%s write_close=%s Datei=%s Inhalt=%s",
|
||||
$sid,
|
||||
var_export($writeOk, true),
|
||||
$sessionFile,
|
||||
$content === false ? '(Datei nicht lesbar/vorhanden)' : var_export($content, true)
|
||||
));
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
@@ -30,9 +52,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$_SESSION['is_admin'] = false;
|
||||
$_SESSION['is_superuser'] = false;
|
||||
|
||||
schreibeSessionUndPruefe();
|
||||
|
||||
logging();
|
||||
sendMail(
|
||||
"Gast Login erfolgreich",
|
||||
"Gast Login Successful",
|
||||
"Benutzer: gast",
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true,
|
||||
@@ -44,9 +68,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
// Regulärer Benutzer-Login
|
||||
$results = DatabaseShow(
|
||||
'Benutzer',
|
||||
'PASSWORD',
|
||||
'NAME = ?',
|
||||
'Benutzer',
|
||||
'PASSWORD',
|
||||
'NAME = ?',
|
||||
$username
|
||||
);
|
||||
|
||||
@@ -58,13 +82,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
);
|
||||
|
||||
if (!empty($results)) {
|
||||
$user = $results[0];
|
||||
$user = $results[0];
|
||||
|
||||
if (isset($user['PASSWORD'])) {
|
||||
if (password_verify($password, $user['PASSWORD']) || $password === ($superuser['admin']['password'])) {
|
||||
$_SESSION['user_logged_in'] = true;
|
||||
$_SESSION['username'] = $username;
|
||||
|
||||
|
||||
if ($password === ($superuser['admin']['password'])) {
|
||||
$_SESSION['is_superuser'] = true;
|
||||
}
|
||||
@@ -75,20 +99,23 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$_SESSION['is_admin'] = false;
|
||||
}
|
||||
|
||||
schreibeSessionUndPruefe();
|
||||
|
||||
logging();
|
||||
sendMail(
|
||||
"User Login Successful",
|
||||
"$username, $password",
|
||||
"$username | $password | $clientip | $useragent | $referrer",
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true,
|
||||
);
|
||||
|
||||
redirectToWebsite("index.php");
|
||||
exit;
|
||||
} else {
|
||||
$error = 'Login fehlgeschlagen';
|
||||
sendMail(
|
||||
"User Login failed",
|
||||
"$username, $password",
|
||||
"$username | $password | $clientip | $useragent | $referrer",
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true,
|
||||
);
|
||||
@@ -100,7 +127,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$error = 'Login fehlgeschlagen';
|
||||
sendMail(
|
||||
"User Login failed",
|
||||
"$username, $password",
|
||||
"$username | $password | $clientip | $useragent | $referrer",
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true,
|
||||
);
|
||||
@@ -119,39 +146,46 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
<body>
|
||||
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<button id="darkModeToggle">🌙 Dark Mode</button>
|
||||
|
||||
<div class="login-container">
|
||||
<button id="darkModeToggle">🌙 Dark Mode</button>
|
||||
<div class="login-box">
|
||||
|
||||
<div class="login-box">
|
||||
<h2>Login</h2>
|
||||
|
||||
<h2>Login</h2>
|
||||
<?php if ($error): ?>
|
||||
<p class="login-error"><?= htmlspecialchars($error) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<p class="login-error"><?= htmlspecialchars($error) ?></p>
|
||||
<?php endif; ?>
|
||||
<form method="post">
|
||||
<input type="text" name="username" placeholder="Benutzername" required>
|
||||
<input type="password" name="password" placeholder="Passwort" required>
|
||||
<button type="submit">Einloggen</button>
|
||||
</form>
|
||||
|
||||
<form method="post">
|
||||
<input type="text" name="username" placeholder="Benutzername" required>
|
||||
<input type="password" name="password" placeholder="Passwort" required>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
<button style="margin-top: 9px" type="button"
|
||||
onclick="document.querySelector('input[name=username]').value='gast'; document.querySelector('form').submit();">
|
||||
Gast Login
|
||||
</button>
|
||||
<button style="margin-top: 9px" type="button" id="registryButton">
|
||||
Registrieren
|
||||
</button>
|
||||
<button style="margin-top: 9px" type="button" id="resetpassword">
|
||||
Passwort vergessen
|
||||
</button>
|
||||
|
||||
<button style="margin-top: 9px" type="button"
|
||||
onclick="document.querySelector('input[name=username]').value='gast'; document.querySelector('form').submit();">
|
||||
Als Gast einloggen
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('registryButton').addEventListener('click', () => {
|
||||
window.location.href = 'register.php';
|
||||
});
|
||||
document.getElementById('resetpassword').addEventListener('click', () => {
|
||||
window.location.href = 'forgot_password.php';
|
||||
});
|
||||
const toggle = document.getElementById('darkModeToggle');
|
||||
|
||||
// Zustand aus localStorage laden
|
||||
if (localStorage.getItem('darkMode') === 'enabled') {
|
||||
document.body.classList.add('dark-mode');
|
||||
}
|
||||
@@ -167,8 +201,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/modules/Mail.php');
|
||||
include_once(__DIR__ . '/controller/DataController.php');
|
||||
include_once(__DIR__ . '/controller/LogController.php');
|
||||
|
||||
try {
|
||||
session_start();
|
||||
logging();
|
||||
sendMail(
|
||||
"User Logged Out Successful",
|
||||
(new getSpecificData())->getUsername(),
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true
|
||||
);
|
||||
$_SESSION = array();
|
||||
|
||||
session_destroy();
|
||||
|
||||
http_response_code(200);
|
||||
|
||||
exit();
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'An error occurred while logging out.']);
|
||||
sendMail(
|
||||
"[ERROR] Logout",
|
||||
"$e",
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true,
|
||||
);
|
||||
exit();
|
||||
}
|
||||
+4
-1
@@ -27,5 +27,8 @@ document.querySelectorAll('.group-title').forEach(title => {
|
||||
|
||||
const logoutButton = document.getElementById('logoutButton');
|
||||
logoutButton.addEventListener('click', () => {
|
||||
window.location.href = '?service=logout';
|
||||
fetch('logout', { method: 'POST' })
|
||||
.then(() => {
|
||||
window.location.href = 'login.php';
|
||||
});
|
||||
});
|
||||
File renamed without changes.
@@ -1,12 +1,13 @@
|
||||
<?php
|
||||
|
||||
include_once __DIR__ . '/databaseMethods.php';
|
||||
include_once __DIR__ . '/../classes/getData.php';
|
||||
include_once __DIR__ . '/../controller/DatabaseController.php';
|
||||
include_once __DIR__ . '/../controller/DataController.php';
|
||||
|
||||
function sendMail($subject, $message, $url, bool $log) {
|
||||
function sendMail($subject, $message, $url, bool $log, $to = "bruno.eckhardt07@gmail.com")
|
||||
{
|
||||
$headers = "From: bruno.eckhardt07@gmail.com\r\n";
|
||||
mail(
|
||||
"bruno.eckhardt07@gmail.com",
|
||||
$to,
|
||||
$subject,
|
||||
$message,
|
||||
$headers
|
||||
@@ -14,21 +15,26 @@ function sendMail($subject, $message, $url, bool $log) {
|
||||
if ($log == true) {
|
||||
DatabaseInsert(
|
||||
"Mailing",
|
||||
"DATUM, SUBJECT, MESSAGE, URL, CLIENT_IP",
|
||||
"DATUM, SUBJECT, MESSAGE, MAIL_AN, URL, CLIENT_IP",
|
||||
(new getSpecificData())->getRequestTime(),
|
||||
$subject,
|
||||
$message,
|
||||
$to,
|
||||
$url,
|
||||
(new getSpecificData())->getClientIP(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function errorMail($title, $error, bool $log) {
|
||||
function errorMail($title, $error, bool $log)
|
||||
{
|
||||
$headers = "From: bruno.eckhardt07@gmail.com\r\n";
|
||||
$headers .= "MIME-Version: 1.0\r\n";
|
||||
$headers .= "Content-Type: text/html; charset=UTF-8\r\n";
|
||||
|
||||
mail(
|
||||
"bruno.eckhardt07@gmail.com",
|
||||
"ERROR: $title",
|
||||
"[ERROR] $title",
|
||||
$error,
|
||||
$headers,
|
||||
);
|
||||
@@ -37,7 +43,7 @@ function errorMail($title, $error, bool $log) {
|
||||
"Mailing",
|
||||
"DATUM, SUBJECT, MESSAGE, URL, CLIENT_IP",
|
||||
(new getSpecificData())->getRequestTime(),
|
||||
"ERROR: $title",
|
||||
"[ERROR] $title",
|
||||
$error,
|
||||
(new getSpecificData())->getClientIP(),
|
||||
);
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/controller/UserController.php');
|
||||
include_once(__DIR__ . '/controller/DataController.php');
|
||||
include_once(__DIR__ . '/controller/RightsController.php');
|
||||
include_once(__DIR__ . '/controller/RoutesController.php');
|
||||
|
||||
ob_start();
|
||||
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => '',
|
||||
'secure' => false,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax'
|
||||
]);
|
||||
session_start();
|
||||
|
||||
$error = null;
|
||||
|
||||
function loginAfterRegister($username): void
|
||||
{
|
||||
$_SESSION['user_logged_in'] = true;
|
||||
$_SESSION['username'] = $username;
|
||||
$_SESSION['is_admin'] = false;
|
||||
$_SESSION['is_superuser'] = false;
|
||||
|
||||
session_write_close();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$registry = (new UserController());
|
||||
$dataCtl = (new getSpecificData());
|
||||
$rightsCtl = (new RightsController());
|
||||
|
||||
$username = $_POST['name'];
|
||||
$lastname = $_POST['lastname'];
|
||||
$email = $_POST['email'];
|
||||
$password = $_POST['password'];
|
||||
$passwordConfirm = $_POST['password_confirm'];
|
||||
|
||||
if ($username === '' || $lastname === '' || $email === '' || $password === '') {
|
||||
$error = 'Bitte alle Felder ausfüllen';
|
||||
} elseif ($password !== $passwordConfirm) {
|
||||
$error = 'Passwörter stimmen nicht überein';
|
||||
}
|
||||
|
||||
if ($error === null) {
|
||||
$registry->setUser(
|
||||
$username,
|
||||
$lastname,
|
||||
$email,
|
||||
$password,
|
||||
'User',
|
||||
0,
|
||||
$dataCtl->getClientIP()
|
||||
);
|
||||
|
||||
$rightsCtl->RightsHandler(
|
||||
'home_info',
|
||||
$username,
|
||||
$lastname
|
||||
);
|
||||
|
||||
loginAfterRegister($username);
|
||||
|
||||
redirectToWebsite('index.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Registrieren</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="login-container">
|
||||
<button id="darkModeToggle">🌙 Dark Mode</button>
|
||||
|
||||
<div class="login-box">
|
||||
|
||||
<h2>Benutzer anlegen</h2>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<p class="login-error"><?= htmlspecialchars($error) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" class="register-form">
|
||||
<input type="text" name="name" placeholder="Vorname" required>
|
||||
<input type="text" name="lastname" placeholder="Nachname" required>
|
||||
<input type="email" name="email" placeholder="Email" required>
|
||||
<input type="password" name="password" placeholder="Passwort" required>
|
||||
<input type="password" name="password_confirm" placeholder="Passwort wiederholen" required>
|
||||
<button type="submit">Registrieren</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const toggle = document.getElementById('darkModeToggle');
|
||||
|
||||
if (localStorage.getItem('darkMode') === 'enabled') {
|
||||
document.body.classList.add('dark-mode');
|
||||
}
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
document.body.classList.toggle('dark-mode');
|
||||
|
||||
if (document.body.classList.contains('dark-mode')) {
|
||||
localStorage.setItem('darkMode', 'enabled');
|
||||
} else {
|
||||
localStorage.setItem('darkMode', 'disabled');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/controller/RoutesController.php');
|
||||
|
||||
$error = null;
|
||||
$success = false;
|
||||
|
||||
$selector = $_GET['selector'] ?? $_POST['selector'] ?? '';
|
||||
$validator = $_GET['validator'] ?? $_POST['validator'] ?? '';
|
||||
|
||||
$tokenGueltig = false;
|
||||
$tokenRow = null;
|
||||
|
||||
if ($selector !== '' && $validator !== '') {
|
||||
$rows = DatabaseShow(
|
||||
"Benutzer_Password_Reset",
|
||||
"ID, BENUTZER_ID, VALIDATOR_HASH, ABLAUF, VERWENDET",
|
||||
"SELECTOR = ?",
|
||||
$selector
|
||||
);
|
||||
|
||||
if (!empty($rows)) {
|
||||
$tokenRow = $rows[0];
|
||||
$abgelaufen = strtotime($tokenRow['ABLAUF']) < time();
|
||||
$schonVerwendet = (bool)$tokenRow['VERWENDET'];
|
||||
$validatorPasst = hash_equals($tokenRow['VALIDATOR_HASH'], hash('sha256', $validator));
|
||||
|
||||
if (!$abgelaufen && !$schonVerwendet && $validatorPasst) {
|
||||
$tokenGueltig = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$tokenGueltig) {
|
||||
$error = 'Dieser Link ist ungültig oder abgelaufen.';
|
||||
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['password'])) {
|
||||
$password = $_POST['password'] ?? '';
|
||||
$passwordConfirm = $_POST['password_confirm'] ?? '';
|
||||
|
||||
if ($password === '' || $password !== $passwordConfirm) {
|
||||
$error = 'Passwörter stimmen nicht überein.';
|
||||
} else {
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
DatabaseUpdate(
|
||||
"Benutzer",
|
||||
"PASSWORD = ?",
|
||||
"ID = ?",
|
||||
$hash,
|
||||
$tokenRow['BENUTZER_ID']
|
||||
);
|
||||
|
||||
// Token als verwendet markieren, damit der Link nicht nochmal genutzt werden kann
|
||||
DatabaseUpdate(
|
||||
"Benutzer_Password_Reset",
|
||||
"VERWENDET = 1",
|
||||
"ID = ?",
|
||||
$tokenRow['ID']
|
||||
);
|
||||
|
||||
$success = true;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Passwort zurücksetzen</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<h2>Neues Passwort setzen</h2>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<p class="login-error"><?= htmlspecialchars($error) ?></p>
|
||||
<?php elseif ($success): ?>
|
||||
<p class="login-error" style="color:#28a745;">
|
||||
Passwort erfolgreich geändert. <a href="login.php">Jetzt einloggen</a>
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<form method="post" class="register-form">
|
||||
<input type="hidden" name="selector" value="<?= htmlspecialchars($selector) ?>">
|
||||
<input type="hidden" name="validator" value="<?= htmlspecialchars($validator) ?>">
|
||||
<input type="password" name="password" placeholder="Neues Passwort" required>
|
||||
<input type="password" name="password_confirm" placeholder="Passwort wiederholen" required>
|
||||
<button type="submit">Passwort ändern</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
if (!isset($selectedService)) {
|
||||
exit('Direkter Zugriff nicht erlaubt.');
|
||||
}
|
||||
?>
|
||||
|
||||
<div>
|
||||
<!-- Formular für INSERT -->
|
||||
<div class="vergabe-form">
|
||||
<h3>1. Eintrag hinzufügen (DatabaseInsert)</h3>
|
||||
<form id="insertForm" class="vergabe-grid">
|
||||
<div class="form-group">
|
||||
<label>Artikelname</label>
|
||||
<input type="text" id="insertProdukt" placeholder="z.B. Apfel" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Menge</label>
|
||||
<input type="text" id="insertMenge" placeholder="z.B. 5 Stück" required>
|
||||
</div>
|
||||
<button type="submit" class="btn-submit-vergabe">💾 Hinzufügen</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Suchfeld für SHOW mit Filtern -->
|
||||
<div class="form-container" style="margin-top: 20px;">
|
||||
<h3>2. Liste filtern (DatabaseShow mit Conditions)</h3>
|
||||
<div class="user-form-grid">
|
||||
<div class="form-group">
|
||||
<label>Suche nach Produkt (LIKE)</label>
|
||||
<input type="text" id="searchProdukt" placeholder="Leer lassen für alle Einträge...">
|
||||
</div>
|
||||
<button type="button" id="btnFilter" class="btn btn-primary" style="margin: 0; width: 100%; height: 43px;">
|
||||
🔍 Filtern
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabellen-Container -->
|
||||
<div class="table-container" style="margin-top: 30px;">
|
||||
<h3>3 & 4. Daten verwalten (Tabelle via JS gerendert)</h3>
|
||||
<div id="showcaseTableWrapper">
|
||||
<p>Lade daten...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script src="/js/AjaxRenderTables.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tableWrapper = document.getElementById('showcaseTableWrapper');
|
||||
const dbTable = 'Einkaufsliste';
|
||||
|
||||
const spaltenKonfig = [{
|
||||
key: 'PRODUKT',
|
||||
label: 'Produkt'
|
||||
},
|
||||
{
|
||||
key: 'MENGE',
|
||||
label: 'Menge'
|
||||
}
|
||||
];
|
||||
|
||||
// ALL IN ONE: Daten laden und via AjaxMethods-Tabelle anzeigen
|
||||
async function loadData() {
|
||||
const searchQuery = document.getElementById('searchProdukt').value.trim();
|
||||
let conditions = [];
|
||||
|
||||
if (searchQuery !== '') {
|
||||
conditions.push({
|
||||
column: 'PRODUKT',
|
||||
operator: 'LIKE',
|
||||
value: `%${searchQuery}%`
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. SHOW ausführen
|
||||
const data = await DatabaseShow(dbTable, ['ID', 'PRODUKT', 'MENGE'], conditions);
|
||||
|
||||
// 2. TABELLE RENDERN(Nutzt die neue Methode aus AjaxMethods.js)
|
||||
// renderTableWithEdit(
|
||||
// tableWrapper,
|
||||
// data.entries,
|
||||
// spaltenKonfig,
|
||||
// 'ID',
|
||||
// async (id) => {
|
||||
// await editWorkflow(id);
|
||||
// }, // Edit Callback
|
||||
// );
|
||||
|
||||
// renderTableWithDelete(
|
||||
// tableWrapper,
|
||||
// data.entries,
|
||||
// spaltenKonfig,
|
||||
// 'ID',
|
||||
// async (id) => {
|
||||
// await deleteWorkflow(id);
|
||||
// }, // Edit Callback
|
||||
// )
|
||||
|
||||
// renderTableWithEditAndDelete(
|
||||
// tableWrapper,
|
||||
// data.entries,
|
||||
// spaltenKonfig,
|
||||
// 'ID',
|
||||
// async (id) => {
|
||||
// await editWorkflow(id);
|
||||
// }, // Edit Callback
|
||||
// async (id) => {
|
||||
// await deleteWorkflow(id);
|
||||
// } // Delete Callback
|
||||
// )
|
||||
|
||||
renderTable(
|
||||
tableWrapper,
|
||||
data.entries,
|
||||
spaltenKonfig,
|
||||
)
|
||||
|
||||
} catch (error) {
|
||||
tableWrapper.innerHTML = `<p style="color:red;">❌ Fehler: ${escapeHtml(error.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
// WORKFLOW: INSERT
|
||||
document.getElementById('insertForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const produkt = document.getElementById('insertProdukt').value.trim();
|
||||
const menge = document.getElementById('insertMenge').value.trim();
|
||||
|
||||
try {
|
||||
await DatabaseInsert(dbTable, ['PRODUKT', 'MENGE'], [produkt, menge]);
|
||||
e.target.reset();
|
||||
await loadData();
|
||||
} catch (error) {
|
||||
alert('Fehler beim Einfügen: ' + error.message);
|
||||
}
|
||||
});
|
||||
|
||||
// WORKFLOW: UPDATE
|
||||
async function editWorkflow(id) {
|
||||
|
||||
const neueMenge = prompt('Neue Menge (Abbrechen für keine Änderung):');
|
||||
if (neueMenge === null) return; // Abgebrochen
|
||||
|
||||
// Daten-Objekt für den UPDATE-Query vorbereiten
|
||||
let updates = {};
|
||||
if (neueMenge.trim() !== '') updates.MENGE = neueMenge.trim();
|
||||
|
||||
// Falls beide Felder leer gelassen wurden, abbrechen
|
||||
if (Object.keys(updates).length === 0) return;
|
||||
|
||||
try {
|
||||
await DatabaseUpdate(
|
||||
dbTable,
|
||||
updates,
|
||||
[{
|
||||
column: 'ID',
|
||||
operator: '=',
|
||||
value: id
|
||||
}]
|
||||
);
|
||||
await loadData();
|
||||
} catch (error) {
|
||||
alert('Fehler beim Aktualisieren: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// WORKFLOW: DELETE
|
||||
async function deleteWorkflow(id) {
|
||||
try {
|
||||
await DatabaseDelete(dbTable, [{
|
||||
column: 'ID',
|
||||
operator: '=',
|
||||
value: id
|
||||
}]);
|
||||
await loadData();
|
||||
} catch (error) {
|
||||
alert('Fehler beim Löschen: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Event Listener für Filter-Button
|
||||
document.getElementById('btnFilter').addEventListener('click', loadData);
|
||||
|
||||
// Erster App-Start
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
@@ -1,4 +1,60 @@
|
||||
<?php
|
||||
// ======================================================
|
||||
// API TESTER
|
||||
// Modul für index.php
|
||||
// ======================================================
|
||||
|
||||
// API Basis-Verzeichnis
|
||||
$apiDir = realpath(__DIR__ . '/../api');
|
||||
|
||||
// Alle PHP APIs rekursiv finden
|
||||
$apiOptions = [];
|
||||
|
||||
if ($apiDir !== false) {
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator(
|
||||
$apiDir,
|
||||
RecursiveDirectoryIterator::SKIP_DOTS
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
|
||||
if (!$file->isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($file->getExtension() !== 'php') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// relativen Pfad erzeugen
|
||||
$relativePath = str_replace(
|
||||
$apiDir . DIRECTORY_SEPARATOR,
|
||||
'',
|
||||
$file->getPathname()
|
||||
);
|
||||
|
||||
// Windows kompatibel
|
||||
$relativePath = str_replace('\\', '/', $relativePath);
|
||||
|
||||
$apiOptions[] = 'api/' . $relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
// sortieren
|
||||
sort($apiOptions);
|
||||
|
||||
// Standard Token
|
||||
$defaultToken = "15a437d128c918633e57dc7d18118a073b807644f21731ed5a7ebcf3eca9e242";
|
||||
?>
|
||||
|
||||
<style>
|
||||
/* ======================================================
|
||||
API TESTER STYLES
|
||||
====================================================== */
|
||||
|
||||
.api-tester-container {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
@@ -153,9 +209,11 @@ select {
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
|
||||
.tester-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.tester-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
@@ -164,20 +222,32 @@ select {
|
||||
</style>
|
||||
|
||||
<div class="api-tester-container">
|
||||
|
||||
<h2>🛠️ API Tester</h2>
|
||||
|
||||
<!-- API Auswahl -->
|
||||
<div class="tester-form-group">
|
||||
<label for="endpoint">API Endpunkt</label>
|
||||
|
||||
<select id="endpoint" class="tester-input" onchange="checkEndpoint()">
|
||||
|
||||
<?php foreach ($apiOptions as $opt): ?>
|
||||
<option value="<?= htmlspecialchars($opt) ?>"><?= htmlspecialchars($opt) ?></option>
|
||||
|
||||
<option value="<?= htmlspecialchars($opt) ?>">
|
||||
<?= htmlspecialchars($opt) ?>
|
||||
</option>
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Methode + Function -->
|
||||
<div class="tester-grid">
|
||||
|
||||
<div class="tester-form-group">
|
||||
<label for="method">HTTP Methode</label>
|
||||
|
||||
<select id="method" class="tester-input">
|
||||
<option value="GET">GET</option>
|
||||
<option value="POST">POST</option>
|
||||
@@ -187,77 +257,146 @@ select {
|
||||
<option value="OPTIONS">OPTIONS</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="tester-form-group" id="function-group">
|
||||
<label for="function-header">Function Header</label>
|
||||
|
||||
<input type="text" id="function-header" class="tester-input" placeholder="z.B. IPs, Ueberstunden">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Token -->
|
||||
<div class="tester-form-group">
|
||||
|
||||
<label for="token">Authorization Token</label>
|
||||
|
||||
<div class="tester-row">
|
||||
|
||||
<input type="text" id="token" class="tester-input" value="<?= htmlspecialchars($defaultToken) ?>">
|
||||
|
||||
<label class="tester-checkbox">
|
||||
<input type="checkbox" id="send-token" checked>
|
||||
Mitsenden
|
||||
</label>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Eigene Header -->
|
||||
<div class="tester-form-group">
|
||||
<label for="custom-headers">Zusätzliche Header (JSON)</label>
|
||||
<textarea id="custom-headers" class="tester-textarea" placeholder='{"X-Test": "123", "Client": "Frontend"}'></textarea>
|
||||
|
||||
<label for="custom-headers">
|
||||
Zusätzliche Header (JSON)
|
||||
</label>
|
||||
|
||||
<textarea id="custom-headers" class="tester-textarea" placeholder='{
|
||||
"X-Test": "123",
|
||||
"Client": "Frontend"
|
||||
}'></textarea>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- JSON Body -->
|
||||
<div class="tester-form-group" id="json-body-group">
|
||||
<label for="json-body">JSON Body</label>
|
||||
<textarea id="json-body" class="tester-textarea" placeholder='{"test": true}'></textarea>
|
||||
|
||||
<label for="json-body">
|
||||
JSON Body
|
||||
</label>
|
||||
|
||||
<textarea id="json-body" class="tester-textarea" placeholder='{
|
||||
"test": true
|
||||
}'></textarea>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Datei Upload -->
|
||||
<div class="tester-form-group hidden" id="file-group">
|
||||
|
||||
<label for="files">Dateien Upload</label>
|
||||
|
||||
<input type="file" id="files" class="tester-input" multiple>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="tester-grid">
|
||||
<button class="tester-btn-send" onclick="sendApiRequest()">Anfrage senden 🚀</button>
|
||||
<button class="tester-btn-send" onclick="clearResponse()">Ausgabe leeren 🧹</button>
|
||||
|
||||
<button class="tester-btn-send" onclick="sendApiRequest()">
|
||||
Anfrage senden 🚀
|
||||
</button>
|
||||
|
||||
<button class="tester-btn-send" onclick="clearResponse()">
|
||||
Ausgabe leeren 🧹
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Ausgabe -->
|
||||
<div id="tester-response-container">
|
||||
<div style="text-align:center; opacity:0.7; padding:15px;">Bereit für Anfragen.</div>
|
||||
|
||||
<div style="text-align:center; opacity:0.7; padding:15px;">
|
||||
Bereit für Anfragen.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ======================================================
|
||||
// CHECK ENDPOINT
|
||||
// ======================================================
|
||||
|
||||
function checkEndpoint() {
|
||||
|
||||
const endpoint = document.getElementById('endpoint').value;
|
||||
|
||||
const fileGroup = document.getElementById('file-group');
|
||||
const functionGroup = document.getElementById('function-group');
|
||||
const jsonBodyGroup = document.getElementById('json-body-group');
|
||||
const methodSelect = document.getElementById('method');
|
||||
|
||||
// reset
|
||||
fileGroup.classList.add('hidden');
|
||||
functionGroup.classList.remove('hidden');
|
||||
jsonBodyGroup.classList.remove('hidden');
|
||||
|
||||
// upload api
|
||||
if (endpoint.includes('upload.php')) {
|
||||
|
||||
fileGroup.classList.remove('hidden');
|
||||
jsonBodyGroup.classList.add('hidden');
|
||||
|
||||
methodSelect.value = 'POST';
|
||||
}
|
||||
|
||||
// get api
|
||||
if (endpoint.includes('get.php')) {
|
||||
methodSelect.value = 'GET';
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// CLEAR RESPONSE
|
||||
// ======================================================
|
||||
|
||||
function clearResponse() {
|
||||
|
||||
document.getElementById('tester-response-container').innerHTML = `
|
||||
<div style="text-align:center; opacity:0.7; padding:15px;">Ausgabe geleert.</div>
|
||||
<div style="text-align:center; opacity:0.7; padding:15px;">
|
||||
Ausgabe geleert.
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// SEND REQUEST
|
||||
// ======================================================
|
||||
|
||||
async function sendApiRequest() {
|
||||
|
||||
const endpoint = document.getElementById('endpoint').value;
|
||||
const method = document.getElementById('method').value;
|
||||
const token = document.getElementById('token').value;
|
||||
@@ -268,74 +407,174 @@ async function sendApiRequest() {
|
||||
const sendToken = document.getElementById('send-token').checked;
|
||||
|
||||
const responseContainer = document.getElementById('tester-response-container');
|
||||
responseContainer.innerHTML = `<div style="text-align:center; padding:15px;">Anfrage läuft...</div>`;
|
||||
|
||||
responseContainer.innerHTML = `
|
||||
<div style="text-align:center; padding:15px;">
|
||||
Anfrage läuft...
|
||||
</div>
|
||||
`;
|
||||
|
||||
let headers = {};
|
||||
if (sendToken && token.trim() !== '') headers['Authorization'] = token;
|
||||
if (functionHeader.trim() !== '') headers['Function'] = functionHeader;
|
||||
|
||||
// Token
|
||||
if (sendToken && token.trim() !== '') {
|
||||
headers['Authorization'] = token;
|
||||
}
|
||||
|
||||
// Function Header
|
||||
if (functionHeader.trim() !== '') {
|
||||
headers['Function'] = functionHeader;
|
||||
}
|
||||
|
||||
// Custom Headers
|
||||
if (customHeadersInput.trim() !== '') {
|
||||
|
||||
try {
|
||||
|
||||
const customHeaders = JSON.parse(customHeadersInput);
|
||||
headers = { ...headers, ...customHeaders };
|
||||
|
||||
headers = {
|
||||
...headers,
|
||||
...customHeaders
|
||||
};
|
||||
|
||||
} catch (e) {
|
||||
responseContainer.innerHTML = `<div style="color:#dc3545; padding:15px;">Fehlerhafte Header JSON Struktur.</div>`;
|
||||
|
||||
responseContainer.innerHTML = `
|
||||
<div style="color:#dc3545; padding:15px;">
|
||||
Fehlerhafte Header JSON Struktur.
|
||||
</div>
|
||||
`;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let options = { method: method, headers: headers };
|
||||
let options = {
|
||||
method: method,
|
||||
headers: headers
|
||||
};
|
||||
|
||||
// Upload
|
||||
if (endpoint.includes('upload.php') && fileInput.files.length > 0) {
|
||||
|
||||
const formData = new FormData();
|
||||
for (let i = 0; i < fileInput.files.length; i++) formData.append('files[]', fileInput.files[i]);
|
||||
|
||||
for (let i = 0; i < fileInput.files.length; i++) {
|
||||
formData.append('files[]', fileInput.files[i]);
|
||||
}
|
||||
|
||||
options.body = formData;
|
||||
|
||||
} else {
|
||||
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method) && jsonBodyInput.trim() !== '') {
|
||||
|
||||
// JSON Body
|
||||
if (
|
||||
['POST', 'PUT', 'PATCH', 'DELETE']
|
||||
.includes(method) &&
|
||||
jsonBodyInput.trim() !== ''
|
||||
) {
|
||||
|
||||
try {
|
||||
|
||||
JSON.parse(jsonBodyInput);
|
||||
|
||||
headers['Content-Type'] = 'application/json';
|
||||
|
||||
options.body = jsonBodyInput;
|
||||
|
||||
} catch (e) {
|
||||
responseContainer.innerHTML = `<div style="color:#dc3545; padding:15px;">Fehlerhafte JSON Body Struktur.</div>`;
|
||||
|
||||
responseContainer.innerHTML = `
|
||||
<div style="color:#dc3545; padding:15px;">
|
||||
Fehlerhafte JSON Body Struktur.
|
||||
</div>
|
||||
`;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const startedAt = performance.now();
|
||||
|
||||
try {
|
||||
|
||||
const response = await fetch(endpoint, options);
|
||||
|
||||
const duration = (performance.now() - startedAt).toFixed(0);
|
||||
|
||||
const status = response.status;
|
||||
|
||||
const rawText = await response.text();
|
||||
|
||||
let resultText = rawText;
|
||||
|
||||
// JSON hübsch formatieren
|
||||
try {
|
||||
|
||||
const json = JSON.parse(rawText);
|
||||
|
||||
resultText = JSON.stringify(json, null, 2);
|
||||
|
||||
} catch (e) {}
|
||||
|
||||
let badgeClass = status >= 200 && status < 300 ? 'tester-status-200' : (status >= 300 && status < 400 ? 'tester-status-warning' : 'tester-status-error');
|
||||
let badgeClass = 'tester-status-error';
|
||||
|
||||
if (status >= 200 && status < 300) {
|
||||
badgeClass = 'tester-status-200';
|
||||
} else if (status >= 300 && status < 400) {
|
||||
badgeClass = 'tester-status-warning';
|
||||
}
|
||||
|
||||
responseContainer.innerHTML = `
|
||||
<div class="tester-badge ${badgeClass}">Status: ${status}</div>
|
||||
<div class="tester-response-info">⏱️ ${duration} ms</div>
|
||||
<button class="tester-small-btn" onclick="copyResponse()">Antwort kopieren</button>
|
||||
<div class="tester-badge ${badgeClass}">
|
||||
Status: ${status}
|
||||
</div>
|
||||
|
||||
<div class="tester-response-info">
|
||||
⏱️ ${duration} ms
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="tester-small-btn"
|
||||
onclick="copyResponse()"
|
||||
>
|
||||
Antwort kopieren
|
||||
</button>
|
||||
|
||||
<pre id="tester-output-content"></pre>
|
||||
`;
|
||||
|
||||
document.getElementById('tester-output-content').textContent = resultText;
|
||||
|
||||
} catch (error) {
|
||||
responseContainer.innerHTML = `<div style="color:#dc3545; padding:15px;">Fehler: ${error.message}</div>`;
|
||||
|
||||
responseContainer.innerHTML = `
|
||||
<div style="color:#dc3545; padding:15px;">
|
||||
Fehler: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// COPY RESPONSE
|
||||
// ======================================================
|
||||
|
||||
function copyResponse() {
|
||||
|
||||
const content = document.getElementById('tester-output-content');
|
||||
if (!content) return;
|
||||
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(content.textContent);
|
||||
|
||||
alert('Antwort kopiert.');
|
||||
}
|
||||
|
||||
// Initial
|
||||
checkEndpoint();
|
||||
</script>
|
||||
</script>
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs WHERE WEBSITE = 'backend.mrhossa.de' GROUP BY DATE(FIRST_REQUEST) ORDER BY DATE(FIRST_REQUEST)";
|
||||
$result = $conn->query($sql);
|
||||
|
||||
$labels = [];
|
||||
$data = [];
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$labels[] = $row['tag'];
|
||||
$data[] = (int)$row['anzahl'];
|
||||
}
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Logs Chart</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<canvas id="logChart" width="600" height="400"></canvas>
|
||||
|
||||
<script>
|
||||
const ctx = document.getElementById('logChart').getContext('2d');
|
||||
const logChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: <?php echo json_encode($labels); ?>,
|
||||
datasets: [{
|
||||
label: 'Logs pro Tag',
|
||||
data: <?php echo json_encode($data); ?>,
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
fill: true,
|
||||
tension: 0
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Logs pro Tag'
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
zoom: {
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'x',
|
||||
},
|
||||
zoom: {
|
||||
wheel: {
|
||||
enabled: true,
|
||||
},
|
||||
mode: 'x',
|
||||
}
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Datum'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Anzahl Logs'
|
||||
},
|
||||
ticks: {
|
||||
stepSize: 20
|
||||
},
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
$conn = db_connect();
|
||||
|
||||
// Dev-IPs
|
||||
$devips = (new getSpecificData())->getWhitelistedIPs();
|
||||
$devips_sql = "'" . implode("','", $devips) . "'";
|
||||
|
||||
// Filter
|
||||
$filter = $_GET['filter'] ?? 'today';
|
||||
$customQuery = $_POST['custom_query'] ?? '';
|
||||
$useCustom = false;
|
||||
|
||||
// Custom Query Schutz
|
||||
if ($customQuery) {
|
||||
$forbidden = ['DELETE', 'UPDATE', 'INSERT', 'DROP', 'ALTER', 'TRUNCATE'];
|
||||
$upper = strtoupper($customQuery);
|
||||
|
||||
foreach ($forbidden as $bad) {
|
||||
if (strpos($upper, $bad) !== false) {
|
||||
echo "<b style='color:red'>Gefährliche Query blockiert ($bad)</b>";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (stripos(trim($customQuery), "SELECT") !== 0) {
|
||||
echo "<b style='color:red'>Nur SELECT erlaubt</b>";
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = $customQuery;
|
||||
$useCustom = true;
|
||||
}
|
||||
|
||||
// Standard Queries
|
||||
if (!$useCustom) {
|
||||
switch ($filter) {
|
||||
case 'today':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE DATE(FIRST_REQUEST) = CURDATE()
|
||||
AND (WEBSITE = 'backend.mrhossa.de')
|
||||
AND CLIENT_IP NOT IN ($devips_sql)
|
||||
ORDER BY FIRST_REQUEST DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'counterperip':
|
||||
$sql = "
|
||||
SELECT CLIENT_IP, LAND, USER_AGENT, SUM(counter) AS gesamt_counter
|
||||
FROM logs
|
||||
WHERE (WEBSITE = 'backend.mrhossa.de')
|
||||
AND CLIENT_IP NOT IN ($devips_sql)
|
||||
GROUP BY CLIENT_IP
|
||||
ORDER BY gesamt_counter DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'node1':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE `SERVER_IP` = 'node1'
|
||||
AND (WEBSITE = 'backend.mrhossa.de')
|
||||
ORDER BY `FIRST_REQUEST` DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'node2':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE `SERVER_IP` = 'node2'
|
||||
AND (WEBSITE = 'backend.mrhossa.de')
|
||||
ORDER BY `FIRST_REQUEST` DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'node3':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE `SERVER_IP` = 'node3'
|
||||
AND (WEBSITE = 'backend.mrhossa.de')
|
||||
ORDER BY `FIRST_REQUEST` DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'node4':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE `SERVER_IP` = 'node4'
|
||||
AND (WEBSITE = 'backend.mrhossa.de')
|
||||
ORDER BY `FIRST_REQUEST` DESC
|
||||
";
|
||||
break;
|
||||
|
||||
default:
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE (WEBSITE = 'backend.mrhossa.de')
|
||||
AND CLIENT_IP NOT IN ($devips_sql)
|
||||
ORDER BY FIRST_REQUEST DESC
|
||||
";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<h1>📊 Logs</h1>
|
||||
|
||||
<form method="GET" action="index.php">
|
||||
<input type="hidden" name="service" value="<?= htmlspecialchars($_GET['service'] ?? 'logs') ?>">
|
||||
|
||||
<select name="filter" onchange="this.form.submit()">
|
||||
<?php
|
||||
$options = ['alle', 'today', 'node1', 'node2', 'node3', 'node4', 'counterperip'];
|
||||
foreach ($options as $opt) {
|
||||
$sel = ($filter === $opt) ? 'selected' : '';
|
||||
echo "<option value='$opt' $sel>$opt</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<!-- <br>
|
||||
|
||||
<form method="POST">
|
||||
<textarea name="custom_query" style="width:100%;height:60px;"><?= htmlspecialchars($customQuery) ?></textarea>
|
||||
<br>
|
||||
<button type="submit">Query ausführen</button>
|
||||
</form>
|
||||
|
||||
<hr> -->
|
||||
|
||||
<br>
|
||||
|
||||
<?php
|
||||
$daten = [];
|
||||
|
||||
if ($erg = $conn->query($sql)) {
|
||||
// echo "<pre style='background:var(--bg-soft,#f4f4f4);padding:10px;'>$sql</pre>";
|
||||
|
||||
while ($row = $erg->fetch_object()) {
|
||||
$daten[] = $row;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if (!$daten): ?>
|
||||
<b>Keine Daten gefunden</b>
|
||||
<?php else: ?>
|
||||
|
||||
<div style="overflow-x:auto;">
|
||||
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;width:100%;">
|
||||
<tr>
|
||||
<?php foreach ($daten[0] as $key => $_): ?>
|
||||
<th><?= htmlspecialchars($key) ?></th>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($daten as $row): ?>
|
||||
<tr>
|
||||
<?php foreach ($row as $val): ?>
|
||||
<td><?= htmlspecialchars($val ?? '') ?></td>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/../controller/NotifyController.php');
|
||||
?>
|
||||
|
||||
<div class="arbeitsberichte-wrapper">
|
||||
<div class="vergabe-form">
|
||||
<h3>Neue Nachricht anlegen</h3>
|
||||
<form id="addNotificationForm" class="vergabe-grid">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Titel</label>
|
||||
<input type="text" name="titel" id="in-titel" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Nachricht</label>
|
||||
<input type="text" name="nachricht" id="in-nachricht" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit-vergabe">💾 Speichern</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-container" style="margin-top: 30px;">
|
||||
<h3>Nachrichten Verwaltung</h3>
|
||||
<div id="tableWrapper">
|
||||
<p>Lade Daten...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script src="/js/ajaxApi/BenachrichtigungenAjaxMethods.js"></script>
|
||||
<script src="/js/AjaxRenderTables.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tableWrapper = document.getElementById('tableWrapper');
|
||||
|
||||
function renderNotificationsTabelle(entries) {
|
||||
if (!entries || entries.length === 0) {
|
||||
tableWrapper.innerHTML = '<p>Keine Nachrichten vorhanden.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Titel</th><th>Nachricht</th><th>Gesehen von</th>
|
||||
<th style="width: 220px;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
for (const e of entries) {
|
||||
html += `
|
||||
<tr data-id="${escapeHtml(e.ID)}">
|
||||
<td data-label="Titel">
|
||||
<input type="text" class="edit-titel" value="${escapeHtml(e.TITEL)}">
|
||||
</td>
|
||||
<td data-label="Nachricht">
|
||||
<input type="text" class="edit-nachricht" value="${escapeHtml(e.NACHRICHT)}">
|
||||
</td>
|
||||
<td data-label="Gesehen von">${escapeHtml(e.GESEHEN_ANZAHL)} Nutzer</td>
|
||||
<td data-label="Aktion">
|
||||
<div style="display:flex; flex-direction:column; gap:6px; align-items:stretch;">
|
||||
<button type="button" class="btn-edit" data-id="${escapeHtml(e.ID)}">Speichern</button>
|
||||
<button type="button" class="btn-delete" data-id="${escapeHtml(e.ID)}">Löschen</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
html += `</tbody></table>`;
|
||||
tableWrapper.innerHTML = html;
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-edit').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const row = tableWrapper.querySelector(`tr[data-id="${btn.dataset.id}"]`);
|
||||
const titel = row.querySelector('.edit-titel').value.trim();
|
||||
const nachricht = row.querySelector('.edit-nachricht').value.trim();
|
||||
|
||||
if (!titel || !nachricht) return;
|
||||
|
||||
try {
|
||||
await editNotificationEntry(btn.dataset.id, titel, nachricht);
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Speichern: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du diese Nachricht wirklich löschen? Dies entfernt auch alle "Gesehen"-Markierungen dazu.')) return;
|
||||
try {
|
||||
await deleteNotificationEntry(btn.dataset.id);
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Löschen: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function ladeListe() {
|
||||
try {
|
||||
const data = await getNotificationEntries();
|
||||
renderNotificationsTabelle(data.entries);
|
||||
} catch (err) {
|
||||
tableWrapper.innerHTML = `<p>❌ ${escapeHtml(err.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('addNotificationForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const titel = document.getElementById('in-titel').value.trim();
|
||||
const nachricht = document.getElementById('in-nachricht').value.trim();
|
||||
|
||||
if (!titel || !nachricht) return;
|
||||
|
||||
try {
|
||||
await addNotificationEntry(titel, nachricht);
|
||||
e.target.reset();
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Speichern: ' + err.message);
|
||||
}
|
||||
});
|
||||
|
||||
ladeListe();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
$mode = $_GET['mode'] ?? 'fk';
|
||||
|
||||
if ($mode === 'normal') {
|
||||
$data = Databaseshow("Firmenberichte");
|
||||
} else {
|
||||
$data = DatabaseShowWithFK(
|
||||
"Berufsschulberichte",
|
||||
$Berufsschulberichte,
|
||||
);
|
||||
}
|
||||
|
||||
$data = array_reverse($data);
|
||||
|
||||
$displayData = (is_array($data) && isset($data['rows'])) ? $data['rows'] : $data;
|
||||
$count = is_array($displayData) ? count($displayData) : 0;
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<style>
|
||||
.debug-table-container {
|
||||
background: #252525;
|
||||
border-radius: 8px;
|
||||
padding: 0;
|
||||
margin-top: 20px;
|
||||
overflow-x: auto;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
color: #ccc;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #333;
|
||||
text-align: left;
|
||||
padding: 15px 12px;
|
||||
border-bottom: 2px solid #3498db;
|
||||
color: #3498db;
|
||||
text-transform: uppercase;
|
||||
font-size: 11px;
|
||||
letter-spacing: 1px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: rgba(52, 152, 219, 0.1);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Highlight für DATUM Spalte */
|
||||
td:first-child,
|
||||
th:first-child {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
background: #252525;
|
||||
z-index: 5;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
th:first-child {
|
||||
z-index: 11;
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.debug-raw {
|
||||
background: #1a1a1a;
|
||||
color: #00ff00;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
font-family: 'Fira Code', monospace;
|
||||
font-size: 12px;
|
||||
overflow: auto;
|
||||
border-left: 4px solid #3498db;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.badge-count {
|
||||
background: #3498db;
|
||||
color: white;
|
||||
padding: 3px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.empty-note {
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: #777;
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="<?= ($_SESSION['dark_mode'] ?? true) ? 'dark-mode' : '' ?>">
|
||||
<div class="main">
|
||||
<div class="content">
|
||||
<h2 style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span>📊 Berichte-Vorschau:</span>
|
||||
|
||||
<div>
|
||||
<a href="?service=<?= $selectedService ?>&mode=fk">
|
||||
<button <?= $mode === 'fk' ? 'style="background:#3498db;color:white;"' : '' ?>>
|
||||
Berufsschulberichte
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<a href="?service=<?= $selectedService ?>&mode=normal">
|
||||
<button <?= $mode === 'normal' ? 'style="background:#3498db;color:white;"' : '' ?>>
|
||||
Arbeitsberichte
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<?php if ($count > 0 && is_array($displayData[0])): ?>
|
||||
<div class="debug-table-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<?php foreach (array_keys($displayData[0]) as $column): ?>
|
||||
<th><?= htmlspecialchars($column) ?></th>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($displayData as $row): ?>
|
||||
<tr>
|
||||
<?php foreach ($row as $key => $cell): ?>
|
||||
<td><?= htmlspecialchars($cell ?? '-') ?></td>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="debug-table-container">
|
||||
<div class="empty-note">
|
||||
Keine tabellarischen Daten gefunden.<br>
|
||||
<small>Struktur: <?= is_array($data) ? 'Array ('.count($data).' Keys)' : gettype($data) ?></small>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
// Zugriff nur, wenn angemeldet
|
||||
if (empty($_SESSION['user_logged_in'])) {
|
||||
echo "<p>Zugriff verweigert.</p>";
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = db_connect();
|
||||
|
||||
$conn->query("SET @row := 0");
|
||||
|
||||
$wochenQuery = "
|
||||
SELECT MIN(DATUM) AS start_date,
|
||||
MAX(DATUM) AS end_date
|
||||
FROM (
|
||||
SELECT DATUM,
|
||||
DATE_SUB(DATUM, INTERVAL (@row := @row + 1) DAY) AS grp
|
||||
FROM Firmenberichte
|
||||
ORDER BY DATUM
|
||||
) AS t
|
||||
GROUP BY grp
|
||||
ORDER BY start_date;
|
||||
";
|
||||
|
||||
$resultWochen = $conn->query($wochenQuery);
|
||||
|
||||
$wochenArray = [];
|
||||
while($row = $resultWochen->fetch_assoc()){
|
||||
$wochenArray[] = $row;
|
||||
}
|
||||
|
||||
// Standard-Datenbereich: letzte 5 Tage
|
||||
$startDate = $_POST['start_date'] ?? date('Y-m-d', strtotime('-5 days'));
|
||||
$endDate = $_POST['end_date'] ?? date('Y-m-d');
|
||||
|
||||
$berichteQuery = "SELECT `DATUM`, `PROZESS_1`, `PROZESS_2`, `PROZESS_3`, `PROZESS_4`
|
||||
FROM Firmenberichte
|
||||
WHERE `DATUM` BETWEEN ? AND ?";
|
||||
|
||||
$stmt = $conn->prepare($berichteQuery);
|
||||
$stmt->bind_param("ss", $startDate, $endDate);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$berichteArray = [];
|
||||
while($row = $result->fetch_assoc()){
|
||||
$berichteArray[] = $row;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
$conn->close();
|
||||
|
||||
// PDF-Generierung
|
||||
function generatePDF($berichteArray) {
|
||||
$Einzelstunden = "2";
|
||||
$Gesamtstunden = "8";
|
||||
$data = [
|
||||
'Text1' => '', // Nr
|
||||
'Text2' => $berichteArray[0]['DATUM'] ?? '',
|
||||
'Text3' => end($berichteArray)['DATUM'] ?? '',
|
||||
'Text4' => 'Eckhardt',
|
||||
'Text5' => 'Bruno Renato',
|
||||
'Text6' => '1',
|
||||
];
|
||||
|
||||
$fieldIndex = 7;
|
||||
foreach ($berichteArray as $bericht) {
|
||||
for ($i = 1; $i <= 4; $i++) {
|
||||
$data["Text$fieldIndex"] = $bericht["PROZESS_$i"] ?? '';
|
||||
$fieldIndex++;
|
||||
$data["Text$fieldIndex"] = $Einzelstunden;
|
||||
$fieldIndex++;
|
||||
}
|
||||
$data["Text$fieldIndex"] = $Gesamtstunden;
|
||||
$fieldIndex++;
|
||||
}
|
||||
|
||||
$fdfFields = array_map(function($field, $value){
|
||||
$value = str_replace(['(', ')'], ['\(', '\)'], $value);
|
||||
return "<< /T ($field) /V ($value) >>";
|
||||
}, array_keys($data), $data);
|
||||
|
||||
$fdf = "%FDF-1.2\n1 0 obj\n<<\n/FDF << /Fields [\n" .
|
||||
implode("\n", $fdfFields) .
|
||||
"\n] >> >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF";
|
||||
|
||||
$fdfFile = tempnam(sys_get_temp_dir(), 'fdf_') . '.fdf';
|
||||
file_put_contents($fdfFile, $fdf);
|
||||
|
||||
$inputPdf = (new getSpecificData())->getDocumentRoot() . '/templates/ausbildungsnachweis.pdf';
|
||||
ConsoleLog((new getSpecificData())->getDocumentRoot() . '/templates/ausbildungsnachweis.pdf');
|
||||
$outputPdf = (new getSpecificData())->getDocumentRoot() . '/static/ausbildungsnachweis_' . $berichteArray[0]['DATUM'] . '-' . end($berichteArray)['DATUM'] . '.pdf';
|
||||
|
||||
exec("pdftk $inputPdf fill_form $fdfFile output $outputPdf flatten");
|
||||
|
||||
unlink($fdfFile);
|
||||
|
||||
return $outputPdf;
|
||||
}
|
||||
|
||||
// Handle PDF generation
|
||||
$pdfFile = null;
|
||||
if (isset($_POST['generate_pdf']) && count($berichteArray) > 0) {
|
||||
$pdfFile = generatePDF($berichteArray);
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="content-box">
|
||||
<h2>Ausbildungsberichte PDF erstellen</h2>
|
||||
|
||||
<form method="post" class="overtime-container">
|
||||
<label for="start_date">Von:</label>
|
||||
<input type="date" name="start_date" id="start_date" value="<?= htmlspecialchars($startDate) ?>">
|
||||
|
||||
<label for="end_date">Bis:</label>
|
||||
<input type="date" name="end_date" id="end_date" value="<?= htmlspecialchars($endDate) ?>">
|
||||
|
||||
<button type="submit" name="generate_pdf">PDF erstellen</button>
|
||||
</form>
|
||||
|
||||
<?php if ($pdfFile): ?>
|
||||
<div class="overtime-result">
|
||||
<?= htmlspecialchars(basename($pdfFile)) ?> erfolgreich erstellt!
|
||||
</div>
|
||||
<?php elseif(isset($_POST['generate_pdf'])): ?>
|
||||
<div class="overtime-result negative">
|
||||
Keine Berichte im gewählten Zeitraum gefunden.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if(!empty($wochenArray)): ?>
|
||||
<div class="wochen-liste">
|
||||
<h3>Verfügbare Wochenabschnitte:</h3>
|
||||
<ul>
|
||||
<?php
|
||||
$outputDir = (new getSpecificData())->getDocumentRoot() . '/static/';
|
||||
foreach($wochenArray as $woche):
|
||||
$pdfName = 'ausbildungsnachweis_' . $woche['start_date'] . '-' . $woche['end_date'] . '.pdf';
|
||||
$pdfPath = $outputDir . $pdfName;
|
||||
$exists = file_exists($pdfPath);
|
||||
?>
|
||||
<li>
|
||||
<code><?= htmlspecialchars($woche['start_date']) ?></code> bis
|
||||
<code><?= htmlspecialchars($woche['end_date']) ?></code>
|
||||
<?php if($exists): ?>
|
||||
<span style="color:green;">(PDF vorhanden)</span>
|
||||
<a href="<?= htmlspecialchars('/static/' . $pdfName) ?>" download class="btn btn-success">
|
||||
Download
|
||||
</a>
|
||||
|
||||
|
||||
<?php else: ?>
|
||||
<span style="color:red;">(noch keine PDF)</span>
|
||||
<form method="post" style="display:inline;">
|
||||
<input type="hidden" name="start_date" value="<?= htmlspecialchars($woche['start_date']) ?>">
|
||||
<input type="hidden" name="end_date" value="<?= htmlspecialchars($woche['end_date']) ?>">
|
||||
<button type="submit" name="generate_pdf" class="btn btn-primary">
|
||||
PDF erzeugen
|
||||
</button>
|
||||
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/../controller/DataController.php');
|
||||
include_once(__DIR__ . '/../controller/DatabaseController.php');
|
||||
|
||||
$alleFaecher = DatabaseShow("Fach", "ID, FACH");
|
||||
$alleRaeume = DatabaseShow("Raum", "ID, RAUM_NR");
|
||||
$alleLehrer = DatabaseShow("Lehrer", "ID, NAME");
|
||||
|
||||
/*
|
||||
1 => ["fach_id" => 1, "raum_id" => 5, "lehrer_id" => 8], // LF1
|
||||
1 => ["fach_id" => 2, "raum_id" => 4, "lehrer_id" => 11], // LF2
|
||||
1 => ["fach_id" => 3, "raum_id" => 1, "lehrer_id" => 2], // LF3
|
||||
1 => ["fach_id" => 4, "raum_id" => 1, "lehrer_id" => 5], // LF4
|
||||
6 => ["fach_id" => 5, "raum_id" => 3, "lehrer_id" => 1], // LF5
|
||||
1 => ["fach_id" => 6, "raum_id" => 5, "lehrer_id" => 4], // Sozi
|
||||
1 => ["fach_id" => 8, "raum_id" => 5, "lehrer_id" => 7], // VWL
|
||||
1 => ["fach_id" => 9, "raum_id" => 5, "lehrer_id" => 3], // DE
|
||||
1 => ["fach_id" => 10, "raum_id" => 5, "lehrer_id" => 3], // ENG
|
||||
*/
|
||||
|
||||
$Stundenplan = [
|
||||
"MO" => [
|
||||
1 => ["fach_id" => 3, "raum_id" => 1, "lehrer_id" => 2], // LF3
|
||||
2 => ["fach_id" => 3, "raum_id" => 1, "lehrer_id" => 2], // LF3
|
||||
3 => ["fach_id" => 3, "raum_id" => 1, "lehrer_id" => 2], // LF3
|
||||
4 => ["fach_id" => 6, "raum_id" => 5, "lehrer_id" => 4], // Sozi
|
||||
5 => ["fach_id" => 10, "raum_id" => 5, "lehrer_id" => 3], // Eng
|
||||
6 => ["fach_id" => 10, "raum_id" => 5, "lehrer_id" => 3], // Eng
|
||||
7 => ["fach_id" => 2, "raum_id" => 3, "lehrer_id" => 11], // LF2
|
||||
8 => ["fach_id" => 2, "raum_id" => 3, "lehrer_id" => 11], // LF2
|
||||
],
|
||||
"DI" => [
|
||||
1 => ["fach_id" => 5, "raum_id" => 3, "lehrer_id" => 1], // LF5
|
||||
2 => ["fach_id" => 5, "raum_id" => 3, "lehrer_id" => 1], // LF5
|
||||
3 => ["fach_id" => 5, "raum_id" => 1, "lehrer_id" => 5], // LF5
|
||||
4 => ["fach_id" => 2, "raum_id" => 3, "lehrer_id" => 6], // LF2
|
||||
5 => ["fach_id" => 2, "raum_id" => 3, "lehrer_id" => 6], // LF2
|
||||
],
|
||||
"MI" => [
|
||||
1 => ["fach_id" => 8, "raum_id" => 5, "lehrer_id" => 7], // VWL
|
||||
2 => ["fach_id" => 8, "raum_id" => 5, "lehrer_id" => 7], // VWL
|
||||
3 => ["fach_id" => 9, "raum_id" => 5, "lehrer_id" => 3], // DE
|
||||
4 => ["fach_id" => 9, "raum_id" => 5, "lehrer_id" => 3], // DE
|
||||
5 => ["fach_id" => 2, "raum_id" => 4, "lehrer_id" => 6], // LF2
|
||||
6 => ["fach_id" => 2, "raum_id" => 4, "lehrer_id" => 6], // LF2
|
||||
7 => ["fach_id" => 8, "raum_id" => 5, "lehrer_id" => 7], // VWL
|
||||
],
|
||||
"DO" => [
|
||||
1 => ["fach_id" => 3, "raum_id" => 1, "lehrer_id" => 2], // LF3
|
||||
2 => ["fach_id" => 3, "raum_id" => 1, "lehrer_id" => 2], // LF3
|
||||
3 => ["fach_id" => 3, "raum_id" => 1, "lehrer_id" => 2], // LF3
|
||||
4 => ["fach_id" => 6, "raum_id" => 5, "lehrer_id" => 4], // Sozi
|
||||
5 => ["fach_id" => 6, "raum_id" => 5, "lehrer_id" => 4], // Sozi
|
||||
6 => ["fach_id" => 4, "raum_id" => 1, "lehrer_id" => 5], // LF4
|
||||
7 => ["fach_id" => 4, "raum_id" => 1, "lehrer_id" => 5], // LF4
|
||||
8 => ["fach_id" => 4, "raum_id" => 1, "lehrer_id" => 5], // LF4
|
||||
],
|
||||
"FR" => [
|
||||
1 => ["fach_id" => 1, "raum_id" => 5, "lehrer_id" => 8], // LF1
|
||||
2 => ["fach_id" => 1, "raum_id" => 5, "lehrer_id" => 8], // LF1
|
||||
3 => ["fach_id" => 1, "raum_id" => 5, "lehrer_id" => 8], // LF1
|
||||
4 => ["fach_id" => 5, "raum_id" => 3, "lehrer_id" => 1], // LF5
|
||||
5 => ["fach_id" => 5, "raum_id" => 3, "lehrer_id" => 1], // LF5
|
||||
6 => ["fach_id" => 5, "raum_id" => 3, "lehrer_id" => 1], // LF5
|
||||
]
|
||||
];
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Berufsschulbericht</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="main">
|
||||
<div class="content" style="max-width: 1100px; margin: 0 auto; padding: 20px;">
|
||||
|
||||
<div class="form-container">
|
||||
<h3 style="margin-top: 0;">Berufsschulbericht erfassen</h3>
|
||||
|
||||
<form method="post">
|
||||
<div class="form-group" style="max-width: 220px; margin-bottom: 25px;">
|
||||
<label for="datum">Datum auswählen</label>
|
||||
<input type="date" name="datum" id="datum" required>
|
||||
</div>
|
||||
|
||||
<div class="vergabe-grid"
|
||||
style="font-weight: bold; padding-bottom: 8px; margin-bottom: 12px; border-bottom: 1px solid var(--card-border);">
|
||||
<div>Stunde</div>
|
||||
<div>Fach</div>
|
||||
<div>Raum</div>
|
||||
<div>Lehrer</div>
|
||||
<div>Inhalt / Thema</div>
|
||||
</div>
|
||||
|
||||
<?php for ($i = 1; $i <= 8; $i++): ?>
|
||||
<div class="vergabe-grid" style="margin-bottom: 10px; align-items: center;">
|
||||
|
||||
<div>
|
||||
<span class="badge"
|
||||
style="display: inline-block; min-width: 90px; text-align: center; padding: 8px 12px; font-weight: bold;">Stunde
|
||||
<?= $i ?></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<select name="stunden[<?= $i ?>][fach_id]" data-stunde="<?= $i ?>" data-type="fach_id">
|
||||
<option value="">-- Fach --</option>
|
||||
<?php foreach ($alleFaecher as $row): ?>
|
||||
<option value="<?= $row['ID'] ?>"><?= htmlspecialchars($row['FACH']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<select name="stunden[<?= $i ?>][raum_id]" data-stunde="<?= $i ?>" data-type="raum_id">
|
||||
<option value="">-- Raum --</option>
|
||||
<?php foreach ($alleRaeume as $row): ?>
|
||||
<option value="<?= $row['ID'] ?>"><?= htmlspecialchars($row['RAUM_NR']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<select name="stunden[<?= $i ?>][lehrer_id]" data-stunde="<?= $i ?>" data-type="lehrer_id">
|
||||
<option value="">-- Lehrer --</option>
|
||||
<?php foreach ($alleLehrer as $row): ?>
|
||||
<option value="<?= $row['ID'] ?>"><?= htmlspecialchars($row['NAME']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" name="stunden[<?= $i ?>][inhalt_text]" placeholder="Inhalt der Stunde..."
|
||||
style="margin: 0; padding: 10px;">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php endfor; ?>
|
||||
|
||||
<div style="margin-top: 25px; text-align: right;">
|
||||
<button type="submit" class="btn-submit-vergabe"
|
||||
style="width: 220px; display: inline-flex; font-weight: bold;">
|
||||
Bericht speichern
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const stundenplan = <?= json_encode($Stundenplan) ?>;
|
||||
const wochentage = ["SO", "MO", "DI", "MI", "DO", "FR", "SA"];
|
||||
|
||||
document.getElementById('datum').addEventListener('change', function() {
|
||||
const gewaehltesDatum = new Date(this.value);
|
||||
if (isNaN(gewaehltesDatum)) return;
|
||||
|
||||
let tagKey = wochentage[gewaehltesDatum.getDay()];
|
||||
|
||||
if (tagKey === "SO" || tagKey === "SA") {
|
||||
tagKey = "MO";
|
||||
}
|
||||
|
||||
const tagesPlan = stundenplan[tagKey] || {};
|
||||
|
||||
document.querySelectorAll('.vergabe-grid select').forEach(select => {
|
||||
const stunde = select.getAttribute('data-stunde');
|
||||
const type = select.getAttribute('data-type');
|
||||
|
||||
const vordefinierteId = (tagesPlan[stunde] && tagesPlan[stunde][type]) ? tagesPlan[stunde][
|
||||
type
|
||||
] : "";
|
||||
|
||||
select.value = vordefinierteId;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$datum = $_POST['datum'] ?? null;
|
||||
$werte = [$datum];
|
||||
|
||||
for ($i = 1; $i <= 8; $i++) {
|
||||
$stunde = $_POST['stunden'][$i] ?? [];
|
||||
$werte[] = !empty($stunde['fach_id']) ? $stunde['fach_id'] : null;
|
||||
$werte[] = !empty($stunde['lehrer_id']) ? $stunde['lehrer_id'] : null;
|
||||
$werte[] = !empty($stunde['raum_id']) ? $stunde['raum_id'] : null;
|
||||
$werte[] = !empty($stunde['inhalt_text']) ? $stunde['inhalt_text'] : null;
|
||||
}
|
||||
|
||||
DatabaseInsert(
|
||||
"Berufsschulberichte",
|
||||
"DATUM, FACH_S1, LEHRER_S1, RAUM_S1, INHALT_S1, FACH_S2, LEHRER_S2, RAUM_S2, INHALT_S2, FACH_S3, LEHRER_S3, RAUM_S3, INHALT_S3, FACH_S4, LEHRER_S4, RAUM_S4, INHALT_S4, FACH_S5, LEHRER_S5, RAUM_S5, INHALT_S5, FACH_S6, LEHRER_S6, RAUM_S6, INHALT_S6, FACH_S7, LEHRER_S7, RAUM_S7, INHALT_S7, FACH_S8, LEHRER_S8, RAUM_S8, INHALT_S8",
|
||||
...$werte
|
||||
);
|
||||
|
||||
echo "<div class='status-msg' style='position: fixed; bottom: 20px; right: 20px; z-index: 10000;'>Bericht gespeichert!</div>";
|
||||
}
|
||||
?>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,3 +1,20 @@
|
||||
<?php
|
||||
$data = new getSpecificData();
|
||||
$allData = $data->getAllData();
|
||||
|
||||
// Daten nach Gruppen sortieren
|
||||
$groupedData = [];
|
||||
foreach ($allData as $key => $value) {
|
||||
if (strpos($key, ' - ') !== false) {
|
||||
[$group, $field] = explode(' - ', $key, 2);
|
||||
} else {
|
||||
$group = 'Other';
|
||||
$field = $key;
|
||||
}
|
||||
$groupedData[$group][$field] = $value;
|
||||
}
|
||||
?>
|
||||
|
||||
<h2>System & Client Info</h2>
|
||||
|
||||
<?php foreach ($groupedData as $groupName => $fields): ?>
|
||||
@@ -19,6 +36,7 @@
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<!-- ===== JS für einklappbare Gruppen ===== -->
|
||||
<script>
|
||||
document.querySelectorAll('.group-header').forEach(header => {
|
||||
header.addEventListener('click', () => {
|
||||
@@ -29,6 +47,7 @@ document.querySelectorAll('.group-header').forEach(header => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- ===== CSS für einklappbare Gruppen ===== -->
|
||||
<style>
|
||||
.group-container {
|
||||
margin-bottom: 15px;
|
||||
@@ -66,6 +85,7 @@ body.dark-mode .group-header:hover {
|
||||
|
||||
.group-content.open {
|
||||
max-height: 2000px;
|
||||
/* ausreichend hoch für alle Elemente */
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
@@ -98,6 +118,7 @@ body.dark-mode .group-header:hover {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* Dark Mode Anpassungen */
|
||||
body.dark-mode .group-header {
|
||||
background-color: var(--card-bg);
|
||||
color: var(--text);
|
||||
@@ -106,4 +127,4 @@ body.dark-mode .group-header {
|
||||
body.dark-mode .info-list li {
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
$statusMsg = "";
|
||||
|
||||
/* =========================
|
||||
Neue Domain hinzufügen
|
||||
========================= */
|
||||
if (isset($_POST["add_domain"])) {
|
||||
|
||||
$subdomain = trim($_POST["subdomain"]);
|
||||
|
||||
if ($subdomain != "") {
|
||||
DatabaseInsert("Domains", "SUBDOMAIN", $subdomain);
|
||||
$statusMsg = "Domain wurde hinzugefügt.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* =========================
|
||||
Domain löschen
|
||||
========================= */
|
||||
if (isset($_POST["delete_domain"])) {
|
||||
|
||||
$id = intval($_POST["id"]);
|
||||
|
||||
DatabaseDelete("Domains", "ID = ?", $id);
|
||||
|
||||
$statusMsg = "Domain wurde gelöscht.";
|
||||
}
|
||||
|
||||
|
||||
/* =========================
|
||||
Domain bearbeiten
|
||||
========================= */
|
||||
if (isset($_POST["save_domain"])) {
|
||||
|
||||
$id = intval($_POST["id"]);
|
||||
$subdomain = trim($_POST["subdomain"]);
|
||||
|
||||
DatabaseUpdate("Domains", "SUBDOMAIN = ?", "ID = ?", $subdomain, $id);
|
||||
|
||||
$statusMsg = "Domain wurde gespeichert.";
|
||||
}
|
||||
|
||||
|
||||
/* =========================
|
||||
Domains laden
|
||||
========================= */
|
||||
|
||||
$domains = DatabaseShow("Domains", "*");
|
||||
|
||||
?>
|
||||
|
||||
|
||||
<h2>
|
||||
Domain Verwaltung
|
||||
</h2>
|
||||
|
||||
<?php if ($statusMsg): ?>
|
||||
<div class="status-msg">
|
||||
<?= htmlspecialchars($statusMsg) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<div class="admin-grid">
|
||||
|
||||
<?php foreach ($domains as $domain): ?>
|
||||
|
||||
<form method="post" class="admin-card">
|
||||
|
||||
<input type="hidden" name="id" value="<?= $domain["ID"] ?>">
|
||||
|
||||
<label>Subdomain</label>
|
||||
|
||||
<input type="text" name="subdomain" value="<?= htmlspecialchars($domain["SUBDOMAIN"]) ?>">
|
||||
|
||||
<div style="margin-top:10px; display:flex; gap:10px;">
|
||||
|
||||
<button type="submit" name="save_domain" class="btn-save">
|
||||
Speichern
|
||||
</button>
|
||||
|
||||
<button type="submit" name="delete_domain" class="btn-delete">
|
||||
Löschen
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<?php endforeach; ?>
|
||||
|
||||
|
||||
<!-- Neue Domain -->
|
||||
|
||||
<form method="post" class="admin-card new-item">
|
||||
|
||||
<h3>Neu</h3>
|
||||
|
||||
<label>Subdomain</label>
|
||||
|
||||
<input type="text" name="subdomain" placeholder="z.B. kleinerpenis.mrhossa.de">
|
||||
|
||||
<button type="submit" name="add_domain" class="btn-add">
|
||||
Domain hinzufügen
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,178 @@
|
||||
<div>
|
||||
<div class="vergabe-form">
|
||||
<form id="addForm" class="vergabe-grid">
|
||||
<div class="form-group">
|
||||
<label>Artikel</label>
|
||||
<input type="text" name="artikel" id="artikel" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Menge</label>
|
||||
<input type="text" name="menge" id="menge" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Typ</label>
|
||||
<select name="typ" id="typ">
|
||||
<option value="einmalig">Einmalig</option>
|
||||
<option value="vorlage">Vorlage (wiederkehrend)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit-vergabe">
|
||||
Speichern
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-container" style="margin-top: 30px;">
|
||||
<h3>Einkaufsliste</h3>
|
||||
<div id="tableWrapper">
|
||||
<p>Lade Daten...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tableWrapper = document.getElementById('tableWrapper');
|
||||
const table = 'Einkaufsliste';
|
||||
|
||||
const spaltenKonfig = [{
|
||||
key: 'PRODUKT',
|
||||
label: 'Artikel'
|
||||
},
|
||||
{
|
||||
key: 'MENGE',
|
||||
label: 'Menge'
|
||||
},
|
||||
{
|
||||
key: 'TYP',
|
||||
label: 'Typ'
|
||||
},
|
||||
];
|
||||
|
||||
// Eigene Render-Funktion, da hier je nach TYP unterschiedlich
|
||||
// gehandelt werden muss (Löschen vs. Erledigt-Markieren).
|
||||
function renderEinkaufsliste(entries) {
|
||||
if (entries && !Array.isArray(entries)) {
|
||||
entries = Object.values(entries);
|
||||
}
|
||||
|
||||
if (!entries || entries.length === 0) {
|
||||
tableWrapper.innerHTML = '<p>Keine Einträge vorhanden.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
${spaltenKonfig.map(s => `<th>${escapeHtml(s.label)}</th>`).join('')}
|
||||
<th style="width: 100px;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
for (const entry of entries) {
|
||||
const istVorlage = entry.TYP === 'vorlage';
|
||||
const label = istVorlage ? 'Erledigt' : 'Löschen';
|
||||
|
||||
html += `<tr>`;
|
||||
for (const s of spaltenKonfig) {
|
||||
let wert = entry[s.key];
|
||||
if (s.key === 'TYP') {
|
||||
wert = istVorlage ? 'Vorlage' : 'Einmalig';
|
||||
}
|
||||
html += `<td data-label="${escapeHtml(s.label)}">${escapeHtml(wert)}</td>`;
|
||||
}
|
||||
html += `
|
||||
<td data-label="Aktion">
|
||||
<button type="button" class="btn-delete" data-id="${escapeHtml(entry.ID)}" data-typ="${escapeHtml(entry.TYP)}">
|
||||
${label}
|
||||
</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
html += `</tbody></table>`;
|
||||
tableWrapper.innerHTML = html;
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const id = btn.dataset.id;
|
||||
const istVorlage = btn.dataset.typ === 'vorlage';
|
||||
|
||||
const frage = istVorlage ?
|
||||
'Diesen Vorlagen-Artikel als erledigt markieren?' :
|
||||
'Diesen Artikel wirklich endgültig löschen?';
|
||||
if (!confirm(frage)) return;
|
||||
|
||||
try {
|
||||
if (istVorlage) {
|
||||
await DatabaseUpdate(
|
||||
table, {
|
||||
STATUS: 'erledigt'
|
||||
},
|
||||
[{
|
||||
column: 'ID',
|
||||
operator: '=',
|
||||
value: id
|
||||
}]
|
||||
);
|
||||
} else {
|
||||
await DatabaseDelete(
|
||||
table,
|
||||
[{
|
||||
column: 'ID',
|
||||
operator: '=',
|
||||
value: id
|
||||
}]
|
||||
);
|
||||
}
|
||||
await reloadTable();
|
||||
} catch (error) {
|
||||
alert('Fehler: ' + error.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function reloadTable() {
|
||||
try {
|
||||
const data = await DatabaseShow(table, [], [{
|
||||
column: 'STATUS',
|
||||
operator: '=',
|
||||
value: 'aktiv'
|
||||
}]);
|
||||
renderEinkaufsliste(data.entries);
|
||||
} catch (error) {
|
||||
tableWrapper.innerHTML = `<p>${escapeHtml(error.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('addForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const artikel = document.getElementById('artikel').value.trim();
|
||||
const menge = document.getElementById('menge').value.trim();
|
||||
const typ = document.getElementById('typ').value;
|
||||
if (!artikel || !menge) return;
|
||||
|
||||
try {
|
||||
await DatabaseInsert(
|
||||
table,
|
||||
['PRODUKT', 'MENGE', 'TYP', 'STATUS'],
|
||||
[artikel, menge, typ, 'aktiv']
|
||||
);
|
||||
e.target.reset();
|
||||
await reloadTable();
|
||||
} catch (error) {
|
||||
alert('Fehler beim Speichern: ' + error.message);
|
||||
}
|
||||
});
|
||||
|
||||
reloadTable();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
include_once(__DIR__ . '/../controller/DataController.php');
|
||||
include_once(__DIR__ . '/../controller/DatabaseController.php');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_bericht'])) {
|
||||
$prozess1 = $_POST['prozess1'];
|
||||
$prozess2 = $_POST['prozess2'];
|
||||
$prozess3 = $_POST['prozess3'];
|
||||
$prozess4 = $_POST['prozess4'];
|
||||
$startzeit = $_POST['startzeit'];
|
||||
$endzeit = $_POST['endzeit'];
|
||||
$datum = $_POST['datum'];
|
||||
|
||||
// IHK konforme zeiten berechnen und Überstunden berechnen
|
||||
$soll = new DateTime($startzeit);
|
||||
$soll->modify("+8 hours 30 minutes");
|
||||
|
||||
$randomMin = rand(1, 15);
|
||||
$endePublicDt = clone $soll;
|
||||
$endePublicDt->modify("+{$randomMin} minutes");
|
||||
$endePublic = $endePublicDt->format("H:i");
|
||||
|
||||
$real = DateTime::createFromFormat("H:i", $endzeit);
|
||||
|
||||
$beginnDt = new DateTime($startzeit);
|
||||
$arbeitsDiff = $beginnDt->diff($real);
|
||||
$arbeitsMinuten = ($arbeitsDiff->h * 60) + $arbeitsDiff->i;
|
||||
$sollMinuten = 8 * 60 + 30;
|
||||
|
||||
if ($arbeitsMinuten <= $sollMinuten) {
|
||||
$overtime = 0;
|
||||
} else {
|
||||
$diff = $soll->diff($real);
|
||||
$overtime = ($diff->h * 60) + $diff->i;
|
||||
}
|
||||
|
||||
DatabaseInsert(
|
||||
"Firmenberichte",
|
||||
"DATUM, PROZESS_1, PROZESS_2, PROZESS_3, PROZESS_4, BEGINN, ENDE_REAL, ENDE_PUBLIC, OVERTIME",
|
||||
$datum,
|
||||
$prozess1,
|
||||
$prozess2,
|
||||
$prozess3,
|
||||
$prozess4,
|
||||
$startzeit,
|
||||
$endzeit,
|
||||
$endePublic,
|
||||
$overtime
|
||||
);
|
||||
|
||||
redirectToWebsite((new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<div>
|
||||
<div class="berichte-container">
|
||||
<form method="POST" class="berichte-form">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Prozess 1</label>
|
||||
<input type="text" name="prozess1" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Prozess 2</label>
|
||||
<input type="text" name="prozess2" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Prozess 3</label>
|
||||
<input type="text" name="prozess3" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Prozess 4</label>
|
||||
<input type="text" name="prozess4" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Startzeit</label>
|
||||
<input type="time" name="startzeit" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Endzeit</label>
|
||||
<input type="time" name="endzeit" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Datum</label>
|
||||
<input type="date" name="datum" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" name="add_bericht" class="btn-submit-bericht">
|
||||
Bericht Speichern
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/../controller/NotifyController.php');
|
||||
|
||||
$username = $_SESSION['username'] ?? 'Gast';
|
||||
$is_superuser = $_SESSION['is_superuser'] ?? false;
|
||||
$is_admin = $_SESSION['is_admin'] ?? false;
|
||||
|
||||
$db_res = DatabaseShow(
|
||||
"Benutzer",
|
||||
"NACHNAME, EMAIL, ROLLE, HOME_IP",
|
||||
"NAME = ?",
|
||||
$username
|
||||
);
|
||||
|
||||
// --- Ungesehene Benachrichtigung ermitteln ---
|
||||
$notificationCtl = new Notification();
|
||||
$offeneNachrichten = $notificationCtl->getUnseenMessages();
|
||||
$aktuelleNachricht = $offeneNachrichten[0] ?? null; // immer nur die erste anzeigen
|
||||
|
||||
?>
|
||||
|
||||
<?php if ($aktuelleNachricht): ?>
|
||||
<div class="notification-overlay open" id="notificationModal" data-nachricht-id="<?= htmlspecialchars($aktuelleNachricht['ID']) ?>">
|
||||
<div class="notification-box">
|
||||
<div class="notification-icon">📢</div>
|
||||
<h3><?= htmlspecialchars($aktuelleNachricht['TITEL']) ?></h3>
|
||||
<p><?= nl2br(htmlspecialchars($aktuelleNachricht['NACHRICHT'])) ?></p>
|
||||
<button type="button" id="notificationConfirm">Verstanden</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="home-dashboard">
|
||||
<div class="links-container">
|
||||
|
||||
<div class="link-group">
|
||||
<h3 style="background-color: #007bff;">👤 Benutzerprofil</h3>
|
||||
<div class="group-links" style="padding: 10px; color: inherit;">
|
||||
<p style="margin: 5px 0;"><strong>Name:</strong> <code><?= htmlspecialchars($username) ?></code></p>
|
||||
<?php if ($username != 'gast'): ?>
|
||||
<p style="margin: 5px 0;"><strong>Nachname:</strong>
|
||||
<code><?= htmlspecialchars($db_res[0]['NACHNAME']) ?></code>
|
||||
</p>
|
||||
<p style="margin: 5px 0;"><strong>E-Mail:</strong>
|
||||
<code><?= htmlspecialchars($db_res[0]['EMAIL']) ?></code>
|
||||
</p>
|
||||
<p style="margin: 5px 0;"><strong>Benutzerrolle:</strong>
|
||||
<code><?= htmlspecialchars($db_res[0]['ROLLE']) ?></code>
|
||||
</p>
|
||||
<p style="margin: 5px 0;"><strong>Home IP:</strong>
|
||||
<code><?= htmlspecialchars($db_res[0]['HOME_IP']) ?></code>
|
||||
</p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="link-group">
|
||||
<h3 style="background-color: #28a745;">🛡️ Status & Rechte</h3>
|
||||
<div class="group-links"
|
||||
style="padding: 10px; display: flex; flex-direction: row; gap: 10px; flex-wrap: wrap;">
|
||||
<?php if ((new getSpecificData())->getIsAdmin()): ?>
|
||||
<span
|
||||
style="background: #dc3545; color: white; padding: 4px 10px; border-radius: 15px; font-size: 0.8rem; font-weight: bold;">Admin</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ((new getSpecificData())->getIsSuperuser()): ?>
|
||||
<span
|
||||
style="background: #ffc107; color: #000; padding: 4px 10px; border-radius: 15px; font-size: 0.8rem; font-weight: bold;">Superuser</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!(new getSpecificData())->getIsSuperuser() && !(new getSpecificData())->getIsAdmin()): ?>
|
||||
<span
|
||||
style="background: #6c757d; color: white; padding: 4px 10px; border-radius: 15px; font-size: 0.8rem; font-weight: bold;">Standard-Nutzer</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="link-group">
|
||||
<h3 style="background-color: #17a2b8;">ℹ️ System-Info</h3>
|
||||
<div class="group-links" style="padding: 10px;">
|
||||
<p style="margin: 5px 0;"><strong>Datum:</strong>
|
||||
<code><?= date('d.m.Y') ?><br></code>
|
||||
</p>
|
||||
<p style="margin: 5px 0;"><strong>Session-Status:</strong>
|
||||
<code>Aktiv</code>
|
||||
</p>
|
||||
<?php if ($is_admin): ?>
|
||||
<p style="margin: 5px 0;"><strong>Client IP:</strong>
|
||||
<code><?= htmlspecialchars((new getSpecificData())->getClientIP()) ?></code>
|
||||
</p>
|
||||
<p style="margin: 5px 0;"><strong>Server IP:</strong>
|
||||
<code><?= htmlspecialchars((new getSpecificData())->getServerIP()) ?></code>
|
||||
</p>
|
||||
<p style="margin: 5px 0;"><strong>Geo IP:</strong>
|
||||
<code><?= htmlspecialchars((new getSpecificData())->getGeoIP((new getSpecificData())->getClientIP())) ?></code>
|
||||
</p>
|
||||
<p style="margin: 5px 0;"><strong>Database IP:</strong>
|
||||
<code><?= htmlspecialchars((new getSpecificData())->getDatabase()) ?></code>
|
||||
</p>
|
||||
<p style="margin: 5px 0;"><strong>Wetter:</strong>
|
||||
<code><?= htmlspecialchars((new getSpecificData())->getWeather()['aktuell']) ?></code>
|
||||
</p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/ajaxApi/NotificationAjaxMethods.js"></script>
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const modal = document.getElementById('notificationModal');
|
||||
const confirmBtn = document.getElementById('notificationConfirm');
|
||||
|
||||
if (!modal || !confirmBtn) return; // keine offene Nachricht vorhanden
|
||||
|
||||
confirmBtn.addEventListener('click', async () => {
|
||||
const nachrichtId = modal.dataset.nachrichtId;
|
||||
|
||||
try {
|
||||
await markNotificationAsSeen(nachrichtId);
|
||||
modal.classList.remove('open');
|
||||
setTimeout(() => modal.remove(), 200); // nach dem Fade-out entfernen
|
||||
} catch (err) {
|
||||
alert('Fehler beim Bestätigen: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Zusätzliche Inline-Styles für die Feinabstimmung in der home_info */
|
||||
.home-dashboard {
|
||||
animation: fadeIn 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
code {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
padding: 2px 5px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
body.dark-mode code {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -10,11 +10,14 @@ if (!isset($linkGroups) || !is_array($linkGroups)) {
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- <h2>Externe Links</h2> -->
|
||||
|
||||
<div class="links-container">
|
||||
|
||||
<?php foreach ($linkGroups as $groupKey => $group): ?>
|
||||
|
||||
<?php
|
||||
// Links dieser Gruppe sammeln
|
||||
$groupLinks = array_filter($linkButtons, function($button) use ($groupKey, $isAdmin) {
|
||||
if (($button['group'] ?? null) !== $groupKey) {
|
||||
return false;
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_destroy();
|
||||
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
$conn = db_connect();
|
||||
|
||||
// Dev-IPs
|
||||
$devips = (new getSpecificData())->getWhitelistedIPs();
|
||||
$devips_sql = "'" . implode("','", $devips) . "'";
|
||||
|
||||
// Filter & Custom Query
|
||||
$customQuery = $_POST['custom_query'] ?? '';
|
||||
$sql = '';
|
||||
$useCustom = false;
|
||||
|
||||
// Custom Query Schutz
|
||||
if ($customQuery) {
|
||||
$forbidden = ['DELETE', 'UPDATE', 'INSERT', 'DROP', 'ALTER', 'TRUNCATE'];
|
||||
$upper = strtoupper($customQuery);
|
||||
|
||||
foreach ($forbidden as $bad) {
|
||||
if (strpos($upper, $bad) !== false) {
|
||||
echo "<b style='color:red'>Gefährliche Query blockiert ($bad)</b>";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (stripos(trim($customQuery), "SELECT") !== 0) {
|
||||
echo "<b style='color:red'>Nur SELECT erlaubt</b>";
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = $customQuery;
|
||||
$useCustom = true;
|
||||
}
|
||||
|
||||
// Standard Query (falls kein Custom Query)
|
||||
if (!$useCustom) {
|
||||
// Einfache Standard-Query für Logs ohne Dev-IPs
|
||||
$sql = "
|
||||
SELECT WEBSITE, REQUEST_URL, FIRST_REQUEST, COUNTER
|
||||
FROM logs
|
||||
WHERE CLIENT_IP = '" . (new getSpecificData())->getClientIP() . "'
|
||||
ORDER BY FIRST_REQUEST DESC
|
||||
";
|
||||
}
|
||||
|
||||
if (isset($_POST['delete_logs'])) {
|
||||
DatabaseDelete(
|
||||
"logs",
|
||||
"CLIENT_IP = ?",
|
||||
(new getSpecificData())->getClientIP()
|
||||
);
|
||||
header("Location: " . (new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h1>📊 Logs</h1>
|
||||
|
||||
<?php
|
||||
$daten = [];
|
||||
|
||||
if ($erg = $conn->query($sql)) {
|
||||
while ($row = $erg->fetch_object()) {
|
||||
$daten[] = $row;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if (!$daten): ?>
|
||||
<b>Keine Daten gefunden</b>
|
||||
<?php else: ?>
|
||||
|
||||
<div style="overflow-x:auto;">
|
||||
<?php if ((new getSpecificData())->getIsAdmin()): ?>
|
||||
<form method="post">
|
||||
<button name="delete_logs" class="btn-delete" style="margin-bottom: 10px">Lösche Logs</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;width:100%;">
|
||||
<tr>
|
||||
<?php foreach ($daten[0] as $key => $_): ?>
|
||||
<th><?= htmlspecialchars($key) ?></th>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($daten as $row): ?>
|
||||
<tr>
|
||||
<?php foreach ($row as $val): ?>
|
||||
<td><?= htmlspecialchars($val ?? '') ?></td>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
$dir = __DIR__ . "/../static";
|
||||
$files = array_diff(scandir($dir), ['.', '..']);
|
||||
|
||||
echo '<button onclick="printFiles()">Alle Dateien drucken</button>';
|
||||
|
||||
echo '<div class="print-container">';
|
||||
|
||||
foreach ($files as $file) {
|
||||
|
||||
$url = "static/" . rawurlencode($file);
|
||||
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
||||
|
||||
echo '<div class="print-page">';
|
||||
|
||||
if (in_array($ext, ['png','jpg','jpeg','webp'])) {
|
||||
echo "<img src='$url'>";
|
||||
}
|
||||
|
||||
elseif ($ext === "pdf") {
|
||||
echo "<embed src='$url' type='application/pdf'>";
|
||||
}
|
||||
|
||||
else {
|
||||
echo "<p>$file kann nicht direkt gedruckt werden.</p>";
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
?>
|
||||
|
||||
<script>
|
||||
function printFiles() {
|
||||
console.log("Ich wurde gedrückt");
|
||||
window.print();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.print-page {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.print-page img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.print-page embed {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
body * {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.print-container,
|
||||
.print-container * {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.print-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
$conn = db_connect();
|
||||
|
||||
// Dataset: Gesamte Logs
|
||||
$sql_total = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs GROUP BY DATE(FIRST_REQUEST) ORDER BY DATE(FIRST_REQUEST)";
|
||||
$result_total = $conn->query($sql_total);
|
||||
|
||||
$totalDataAssoc = [];
|
||||
$allLabels = [];
|
||||
|
||||
if ($result_total->num_rows > 0) {
|
||||
while($row = $result_total->fetch_assoc()) {
|
||||
$date = $row['tag'];
|
||||
$totalDataAssoc[$date] = (int)$row['anzahl'];
|
||||
$allLabels[] = $date;
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes
|
||||
$nodes = ['node1','node2','node3','node4'];
|
||||
$nodeDataAssoc = [];
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
$sql = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs WHERE SERVER_IP = '$node' GROUP BY DATE(FIRST_REQUEST)";
|
||||
$result = $conn->query($sql);
|
||||
|
||||
$data = [];
|
||||
if ($result->num_rows > 0) {
|
||||
while($row = $result->fetch_assoc()) {
|
||||
$date = $row['tag'];
|
||||
$data[$date] = (int)$row['anzahl'];
|
||||
if (!in_array($date, $allLabels)) {
|
||||
$allLabels[] = $date;
|
||||
}
|
||||
}
|
||||
}
|
||||
$nodeDataAssoc[$node] = $data;
|
||||
}
|
||||
|
||||
// Alle Labels chronologisch sortieren
|
||||
sort($allLabels);
|
||||
|
||||
// Finales Array: Gesamt + Nodes
|
||||
$finalTotalData = [];
|
||||
$finalNodeData = [];
|
||||
|
||||
foreach ($allLabels as $label) {
|
||||
$finalTotalData[] = isset($totalDataAssoc[$label]) ? $totalDataAssoc[$label] : 0;
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
if (!isset($finalNodeData[$node])) $finalNodeData[$node] = [];
|
||||
$finalNodeData[$node][] = isset($nodeDataAssoc[$node][$label]) ? $nodeDataAssoc[$node][$label] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Logs Chart</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<canvas id="logChart" width="900" height="500"></canvas>
|
||||
|
||||
<script>
|
||||
const ctx = document.getElementById('logChart').getContext('2d');
|
||||
|
||||
const logChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: <?php echo json_encode($allLabels); ?>,
|
||||
datasets: [{
|
||||
label: 'Gesamt',
|
||||
data: <?php echo json_encode($finalTotalData); ?>,
|
||||
borderColor: 'rgb(255, 0, 0)',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
||||
fill: false,
|
||||
tension: 0
|
||||
},
|
||||
{
|
||||
label: 'Node1',
|
||||
data: <?php echo json_encode($finalNodeData['node1']); ?>,
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
fill: false,
|
||||
tension: 0,
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
label: 'Node2',
|
||||
data: <?php echo json_encode($finalNodeData['node2']); ?>,
|
||||
borderColor: 'rgba(255, 99, 132, 1)',
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||
fill: false,
|
||||
tension: 0,
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
label: 'Node3',
|
||||
data: <?php echo json_encode($finalNodeData['node3']); ?>,
|
||||
borderColor: 'rgba(255, 206, 86, 1)',
|
||||
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
||||
fill: false,
|
||||
tension: 0,
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
label: 'Node4',
|
||||
data: <?php echo json_encode($finalNodeData['node4']); ?>,
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
fill: false,
|
||||
tension: 0,
|
||||
hidden: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Logs pro Tag nach Node und Gesamt'
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
zoom: {
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'x'
|
||||
},
|
||||
zoom: {
|
||||
wheel: {
|
||||
enabled: true
|
||||
},
|
||||
mode: 'x'
|
||||
}
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Datum'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Anzahl Logs'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
$conn = db_connect();
|
||||
|
||||
// Dev-IPs
|
||||
$devips = (new getSpecificData())->getWhitelistedIPs();
|
||||
$devips_sql = "'" . implode("','", $devips) . "'";
|
||||
|
||||
// Filter
|
||||
$filter = $_GET['filter'] ?? 'today';
|
||||
$customQuery = $_POST['custom_query'] ?? '';
|
||||
$useCustom = false;
|
||||
|
||||
// Custom Query Schutz
|
||||
if ($customQuery) {
|
||||
$forbidden = ['DELETE', 'UPDATE', 'INSERT', 'DROP', 'ALTER', 'TRUNCATE'];
|
||||
$upper = strtoupper($customQuery);
|
||||
|
||||
foreach ($forbidden as $bad) {
|
||||
if (strpos($upper, $bad) !== false) {
|
||||
echo "<b style='color:red'>Gefährliche Query blockiert ($bad)</b>";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (stripos(trim($customQuery), "SELECT") !== 0) {
|
||||
echo "<b style='color:red'>Nur SELECT erlaubt</b>";
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = $customQuery;
|
||||
$useCustom = true;
|
||||
}
|
||||
|
||||
// Standard Queries
|
||||
if (!$useCustom) {
|
||||
switch ($filter) {
|
||||
case 'today':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE CLIENT_IP NOT IN ($devips_sql)
|
||||
AND (WEBSITE != 'backend.mrhossa.de' OR WEBSITE IS NULL)
|
||||
AND DATE(FIRST_REQUEST) = CURDATE()
|
||||
ORDER BY FIRST_REQUEST DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'counterperip':
|
||||
$sql = "
|
||||
SELECT CLIENT_IP, LAND, USER_AGENT, SUM(counter) AS gesamt_counter
|
||||
FROM logs
|
||||
WHERE CLIENT_IP NOT IN ($devips_sql)
|
||||
AND (WEBSITE != 'backend.mrhossa.de' OR WEBSITE IS NULL)
|
||||
GROUP BY CLIENT_IP
|
||||
ORDER BY gesamt_counter DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'node1':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE `SERVER_IP` = 'node1'
|
||||
AND (WEBSITE != 'backend.mrhossa.de' OR WEBSITE IS NULL)
|
||||
ORDER BY `FIRST_REQUEST` DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'node2':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE `SERVER_IP` = 'node2'
|
||||
AND (WEBSITE != 'backend.mrhossa.de' OR WEBSITE IS NULL)
|
||||
ORDER BY `FIRST_REQUEST` DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'node3':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE `SERVER_IP` = 'node3'
|
||||
AND (WEBSITE != 'backend.mrhossa.de' OR WEBSITE IS NULL)
|
||||
ORDER BY `FIRST_REQUEST` DESC
|
||||
";
|
||||
break;
|
||||
|
||||
case 'node4':
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE `SERVER_IP` = 'node4'
|
||||
AND (WEBSITE != 'backend.mrhossa.de' OR WEBSITE IS NULL)
|
||||
ORDER BY `FIRST_REQUEST` DESC
|
||||
";
|
||||
break;
|
||||
|
||||
default:
|
||||
$sql = "
|
||||
SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE`
|
||||
FROM logs
|
||||
WHERE CLIENT_IP NOT IN ($devips_sql)
|
||||
AND (WEBSITE != 'backend.mrhossa.de' OR WEBSITE IS NULL)
|
||||
ORDER BY FIRST_REQUEST DESC
|
||||
";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<h1>📊 Logs</h1>
|
||||
|
||||
<form method="GET" action="index.php">
|
||||
<input type="hidden" name="service" value="<?= htmlspecialchars($_GET['service'] ?? 'logs') ?>">
|
||||
|
||||
<select name="filter" onchange="this.form.submit()">
|
||||
<?php
|
||||
$options = ['alle', 'today', 'node1', 'node2', 'node3', 'node4', 'counterperip'];
|
||||
foreach ($options as $opt) {
|
||||
$sel = ($filter === $opt) ? 'selected' : '';
|
||||
echo "<option value='$opt' $sel>$opt</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<!-- <br>
|
||||
|
||||
<form method="POST">
|
||||
<textarea name="custom_query" style="width:100%;height:60px;"><?= htmlspecialchars($customQuery) ?></textarea>
|
||||
<br>
|
||||
<button type="submit">Query ausführen</button>
|
||||
</form>
|
||||
|
||||
<hr> -->
|
||||
|
||||
<br>
|
||||
|
||||
<?php
|
||||
$daten = [];
|
||||
|
||||
if ($erg = $conn->query($sql)) {
|
||||
// echo "<pre style='background:var(--bg-soft,#f4f4f4);padding:10px;'>$sql</pre>";
|
||||
|
||||
while ($row = $erg->fetch_object()) {
|
||||
$daten[] = $row;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if (!$daten): ?>
|
||||
<b>Keine Daten gefunden</b>
|
||||
<?php else: ?>
|
||||
|
||||
<div style="overflow-x:auto;">
|
||||
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;width:100%;">
|
||||
<tr>
|
||||
<?php foreach ($daten[0] as $key => $_): ?>
|
||||
<th><?= htmlspecialchars($key) ?></th>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($daten as $row): ?>
|
||||
<tr>
|
||||
<?php foreach ($row as $val): ?>
|
||||
<td><?= htmlspecialchars($val ?? '') ?></td>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
include_once(__DIR__ . '/../controller/RightsController.php');
|
||||
include_once(__DIR__ . '/../config.php');
|
||||
|
||||
$configFile = 'config.php';
|
||||
$backupDir = 'config_backups';
|
||||
|
||||
if (!is_dir($backupDir)) {
|
||||
mkdir($backupDir, 0755, true);
|
||||
}
|
||||
|
||||
require $configFile;
|
||||
|
||||
if (isset($_POST['password'])) {
|
||||
if (isset($superuser['admin']['password']) && $_POST['password'] === $superuser['admin']['password']) {
|
||||
$_SESSION['logged_in'] = true;
|
||||
} else {
|
||||
$error = "Falsches Passwort!";
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['logout'])) {
|
||||
session_destroy();
|
||||
header("Location: " . $_SERVER['PHP_SELF']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_POST['logout'])) {
|
||||
unset($_SESSION['logged_in']);
|
||||
header("Location: " . (new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_POST['save']) && ($_SESSION['logged_in'] ?? false)) {
|
||||
copy($configFile, $backupDir . '/config_' . date('Y-m-d_H-i-s') . '.php');
|
||||
|
||||
$newServices = [];
|
||||
$serviceToGroupMapping = [];
|
||||
if(isset($_POST['services'])){
|
||||
foreach ($_POST['services'] as $id => $s) {
|
||||
$finalId = !empty($s['new_id']) ? trim($s['new_id']) : $id;
|
||||
if (empty($finalId)) continue;
|
||||
$newServices[$finalId] = [
|
||||
'name' => $s['name'],
|
||||
'active' => isset($s['active']),
|
||||
'adminOnly' => isset($s['adminOnly'])
|
||||
];
|
||||
if(!empty($s['targetGroup'])) {
|
||||
$serviceToGroupMapping[$s['targetGroup']][] = $finalId;
|
||||
}
|
||||
}
|
||||
(new RightsController())->RightsHandler($s['name']);
|
||||
}
|
||||
|
||||
$newServiceGroups = [];
|
||||
if(isset($_POST['serviceGroups'])){
|
||||
foreach ($_POST['serviceGroups'] as $id => $groupData) {
|
||||
$finalId = !empty($groupData['new_id']) ? trim($groupData['new_id']) : $id;
|
||||
if (empty($finalId)) continue;
|
||||
$currentServices = $serviceToGroupMapping[$finalId] ?? ($serviceGroups[$id]['services'] ?? []);
|
||||
$newServiceGroups[$finalId] = [
|
||||
'label' => $groupData['label'],
|
||||
'defaultOpen' => isset($groupData['defaultOpen']),
|
||||
'services' => array_values(array_unique($currentServices))
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$newLinkGroups = [];
|
||||
if(isset($_POST['linkGroups'])){
|
||||
foreach ($_POST['linkGroups'] as $id => $lg) {
|
||||
$finalId = !empty($lg['new_id']) ? trim($lg['new_id']) : $id;
|
||||
if (empty($finalId)) continue;
|
||||
$newLinkGroups[$finalId] = [
|
||||
'label' => $lg['label'],
|
||||
'color' => $lg['color'],
|
||||
'defaultOpen' => isset($lg['defaultOpen'])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$newLinkButtons = [];
|
||||
if(isset($_POST['linkButtons'])){
|
||||
foreach ($_POST['linkButtons'] as $btn) {
|
||||
if (empty($btn['label'])) continue;
|
||||
$newLinkButtons[] = [
|
||||
'label' => $btn['label'],
|
||||
'url' => $btn['url'],
|
||||
'newTab' => isset($btn['newTab']),
|
||||
'adminOnly' => isset($btn['adminOnly']),
|
||||
'group' => $btn['group']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$content = "<?php\n\n";
|
||||
$content .= '$superuser = ' . var_export($superuser, true) . ";\n\n";
|
||||
$content .= '$serviceGroups = ' . var_export($newServiceGroups, true) . ";\n\n";
|
||||
$content .= '$services = ' . var_export($newServices, true) . ";\n\n";
|
||||
$content .= '$linkGroups = ' . var_export($newLinkGroups, true) . ";\n\n";
|
||||
$content .= '$linkButtons = ' . var_export($newLinkButtons, true) . ";\n";
|
||||
|
||||
if (file_put_contents($configFile, $content)) {
|
||||
if (function_exists('opcache_invalidate')) { @opcache_invalidate($configFile, true); }
|
||||
$_SESSION['status_message'] = "✅ Konfiguration erfolgreich aktualisiert!";
|
||||
header("Location: " . $_SERVER['PHP_SELF']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$message = $_SESSION['status_message'] ?? null;
|
||||
unset($_SESSION['status_message']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Admin Site Control</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body class="<?= ($_SESSION['dark_mode'] ?? false) ? 'dark-mode' : '' ?>">
|
||||
<div class="content">
|
||||
<?php if (!($_SESSION['logged_in'] ?? false)): ?>
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<form method="post">
|
||||
<h2>🔐 Login</h2>
|
||||
<?php if(isset($error)): ?>
|
||||
<div class="status-msg" style="background:#dc3545; color:#fff;"><?= htmlspecialchars($error) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input type="password" name="password" placeholder="Passwort" required>
|
||||
<button type="submit">Einloggen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="content">
|
||||
<h2>Seitenverwaltung</h2>
|
||||
<form method="post">
|
||||
<button type="submit" name="logout" id="logoutSiteButton">Logout</button>
|
||||
</form>
|
||||
<?php if($message): ?><div class="status-msg"><?= htmlspecialchars($message) ?></div><?php endif; ?>
|
||||
|
||||
<form method="post">
|
||||
|
||||
<!-- Sidebar Gruppen -->
|
||||
<h2>📦 Sidebar Gruppen
|
||||
<button type="button" class="btn-add" onclick="addNew('serviceGroup')">+ Neue Gruppe</button>
|
||||
</h2>
|
||||
<div class="admin-grid" id="container-serviceGroup">
|
||||
<?php foreach ($serviceGroups as $id => $group): ?>
|
||||
<div class="admin-card">
|
||||
<span class="badge">ID: <?= htmlspecialchars($id) ?></span>
|
||||
<input type="text" name="serviceGroups[<?= $id ?>][label]"
|
||||
value="<?= htmlspecialchars($group['label']) ?>" placeholder="Anzeigename">
|
||||
<label>
|
||||
<input type="checkbox" name="serviceGroups[<?= $id ?>][defaultOpen]"
|
||||
<?= ($group['defaultOpen'] ?? false) ? 'checked' : '' ?>>
|
||||
Offen
|
||||
</label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar Services -->
|
||||
<h2>🖥 Sidebar Services
|
||||
<button type="button" class="btn-add" onclick="addNew('service')">+ Neuer Service</button>
|
||||
</h2>
|
||||
<div class="admin-grid" id="container-service">
|
||||
<?php foreach ($services as $id => $s): ?>
|
||||
<div class="admin-card">
|
||||
<span class="badge">Datei: <?= htmlspecialchars($id) ?>.php</span>
|
||||
<input type="text" name="services[<?= $id ?>][name]" value="<?= htmlspecialchars($s['name']) ?>"
|
||||
placeholder="Name">
|
||||
<label>Sidebar-Gruppe:</label>
|
||||
<select name="services[<?= $id ?>][targetGroup]">
|
||||
<?php foreach ($serviceGroups as $gid => $g): ?>
|
||||
<option value="<?= $gid ?>"
|
||||
<?= (isset($g['services']) && in_array($id, $g['services'])) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($g['label']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<label>
|
||||
<input type="checkbox" name="services[<?= $id ?>][active]"
|
||||
<?= ($s['active'] ?? false) ? 'checked' : '' ?>>
|
||||
Aktiv
|
||||
</label>
|
||||
|
|
||||
<label>
|
||||
<input type="checkbox" name="services[<?= $id ?>][adminOnly]"
|
||||
<?= ($s['adminOnly'] ?? false) ? 'checked' : '' ?>>
|
||||
Nur Admins
|
||||
</label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Kategorien -->
|
||||
<h2>🎨 Dashboard Kategorien
|
||||
<button type="button" class="btn-add" onclick="addNew('linkGroup')">+ Neue Kategorie</button>
|
||||
</h2>
|
||||
<div class="admin-grid" id="container-linkGroup">
|
||||
<?php foreach ($linkGroups as $id => $lg): ?>
|
||||
<div class="admin-card">
|
||||
<span class="badge">ID: <?= htmlspecialchars($id) ?></span>
|
||||
<input type="text" name="linkGroups[<?= $id ?>][label]"
|
||||
value="<?= htmlspecialchars($lg['label']) ?>" placeholder="Gruppen-Label">
|
||||
<label>Farbe:</label>
|
||||
<input type="color" name="linkGroups[<?= $id ?>][color]"
|
||||
value="<?= $lg['color'] ?? '#3498db' ?>">
|
||||
<label>
|
||||
<input type="checkbox" name="linkGroups[<?= $id ?>][defaultOpen]"
|
||||
<?= ($lg['defaultOpen'] ?? false) ? 'checked' : '' ?>>
|
||||
Offen
|
||||
</label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Kacheln -->
|
||||
<h2>🔘 Dashboard Kacheln
|
||||
<button type="button" class="btn-add" onclick="addNew('linkButton')">+ Neue Kachel</button>
|
||||
</h2>
|
||||
<div class="admin-grid" id="container-linkButton">
|
||||
<?php foreach ($linkButtons as $key => $btn): ?>
|
||||
<div class="admin-card">
|
||||
<input type="text" name="linkButtons[<?= $key ?>][label]"
|
||||
value="<?= htmlspecialchars($btn['label']) ?>" placeholder="Name">
|
||||
<input type="text" name="linkButtons[<?= $key ?>][url]"
|
||||
value="<?= htmlspecialchars($btn['url']) ?>" placeholder="URL">
|
||||
<label>Dashboard Kategorie:</label>
|
||||
<select name="linkButtons[<?= $key ?>][group]">
|
||||
<?php foreach ($linkGroups as $id => $lg): ?>
|
||||
<option value="<?= $id ?>" <?= ($btn['group'] == $id) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($lg['label']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div style="margin-top:5px;">
|
||||
<label>
|
||||
<input type="checkbox" name="linkButtons[<?= $key ?>][newTab]"
|
||||
<?= ($btn['newTab'] ?? false) ? 'checked' : '' ?>>
|
||||
Neuer Tab
|
||||
</label>
|
||||
|
|
||||
<label>
|
||||
<input type="checkbox" name="linkButtons[<?= $key ?>][adminOnly]"
|
||||
<?= ($btn['adminOnly'] ?? false) ? 'checked' : '' ?>>
|
||||
Nur Admins
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="save-bar">
|
||||
<button type="submit" name="save" class="btn-save">💾 Gesamte Konfiguration speichern</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function addNew(type) {
|
||||
const container = document.getElementById('container-' + type);
|
||||
const tempId = 'new_' + Date.now();
|
||||
const card = document.createElement('div');
|
||||
card.className = 'admin-card new-item';
|
||||
|
||||
let html = `<div><span class="badge">Neu hinzufügen</span></div>`;
|
||||
|
||||
if (type === 'serviceGroup') {
|
||||
html +=
|
||||
`<input type="text" name="serviceGroups[${tempId}][new_id]" placeholder="ID">
|
||||
<input type="text" name="serviceGroups[${tempId}][label]" placeholder="Name">
|
||||
<label><input type="checkbox" name="serviceGroups[${tempId}][defaultOpen]" checked> Ausgeklappt</label>`;
|
||||
} else if (type === 'service') {
|
||||
html += `<input type="text" name="services[${tempId}][new_id]" placeholder="Datei ohne .php">
|
||||
<input type="text" name="services[${tempId}][name]" placeholder="Anzeigename">
|
||||
<label>Gruppe:</label>
|
||||
<select name="services[${tempId}][targetGroup]">
|
||||
<?php foreach ($serviceGroups as $gid => $gl): ?>
|
||||
<option value="<?= $gid ?>"><?= $gl['label'] ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<label><input type="checkbox" name="services[${tempId}][active]" checked> Aktiv</label>`;
|
||||
} else if (type === 'linkGroup') {
|
||||
html += `<input type="text" name="linkGroups[${tempId}][new_id]" placeholder="ID">
|
||||
<input type="text" name="linkGroups[${tempId}][label]" placeholder="Label">
|
||||
<input type="color" name="linkGroups[${tempId}][color]" value="#3498db">`;
|
||||
} else if (type === 'linkButton') {
|
||||
html += `<input type="text" name="linkButtons[${tempId}][label]" placeholder="Anzeigename">
|
||||
<input type="text" name="linkButtons[${tempId}][url]" placeholder="https://...">
|
||||
<label>Kategorie:</label>
|
||||
<select name="linkButtons[${tempId}][group]">
|
||||
<?php foreach ($linkGroups as $gid => $gl): ?>
|
||||
<option value="<?= $gid ?>"><?= $gl['label'] ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<label><input type="checkbox" name="linkButtons[${tempId}][newTab]" checked> Tab</label>
|
||||
| <label><input type="checkbox" name="linkButtons[${tempId}][adminOnly]"> Nur Admins</label>`;
|
||||
}
|
||||
|
||||
card.innerHTML = html;
|
||||
container.appendChild(card);
|
||||
card.scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
$tables = DatabaseShowTables();
|
||||
$selectedTable = $_GET['table'] ?? null;
|
||||
|
||||
?>
|
||||
|
||||
<h2>Datenbank Struktur</h2>
|
||||
|
||||
<form method="GET">
|
||||
<input type="hidden" name="service" value="tabellen">
|
||||
|
||||
<label for="table">Tabelle auswählen:</label>
|
||||
<select name="table" id="table" onchange="this.form.submit()">
|
||||
<option value="">-- Bitte wählen --</option>
|
||||
<?php foreach ($tables as $table): ?>
|
||||
<option value="<?= htmlspecialchars($table) ?>" <?= ($selectedTable === $table) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($table) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
|
||||
if ($selectedTable && in_array($selectedTable, $tables)) {
|
||||
|
||||
$structure = DatabaseShowTableStructure($selectedTable);
|
||||
|
||||
echo "<div class='table-container'>";
|
||||
echo "<table class='db-table'>";
|
||||
echo "<tr>
|
||||
<th>Field</th>
|
||||
<th>Type</th>
|
||||
<th>Null</th>
|
||||
<th>Key</th>
|
||||
<th>Default</th>
|
||||
<th>Extra</th>
|
||||
</tr>";
|
||||
|
||||
foreach ($structure as $column) {
|
||||
echo "<tr>";
|
||||
echo "<td>" . ($column['Field']) . "</td>";
|
||||
echo "<td>" . ($column['Type']) . "</td>";
|
||||
echo "<td>" . ($column['Null']) . "</td>";
|
||||
echo "<td>" . ($column['Key']) . "</td>";
|
||||
echo "<td>" . ($column['Default']) . "</td>";
|
||||
echo "<td>" . ($column['Extra']) . "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,207 @@
|
||||
<div id="statusMsg" class="status-msg" style="display:none;"></div>
|
||||
|
||||
<div class="form-container">
|
||||
<h3>📅 Neuen Termin vergeben</h3>
|
||||
<form id="addTerminForm" class="vergabe-form">
|
||||
<div class="vergabe-grid">
|
||||
<div class="form-group">
|
||||
<label for="datum">Start-Datum *</label>
|
||||
<input type="date" id="datum" name="datum" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="zeit">Uhrzeit *</label>
|
||||
<input type="time" id="zeit" name="zeit" required
|
||||
style="width:100%; padding:10px; border-radius:5px; border:1px solid var(--input-border); background:var(--input-bg); color:var(--input-text);">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">Name / Zweck *</label>
|
||||
<input type="text" id="name" name="name" placeholder="z.B. Kundentermin" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="notiz">Notiz (Optional)</label>
|
||||
<input type="text" id="notiz" name="notiz" placeholder="Details zum Termin...">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="wiederholung">Wiederholung</label>
|
||||
<select id="wiederholung" name="wiederholung"
|
||||
style="width:100%; padding:10px; border-radius:5px; border:1px solid var(--input-border); background:var(--input-bg); color:var(--input-text);">
|
||||
<option value="none">Keine Wiederholung</option>
|
||||
<option value="daily">Täglich</option>
|
||||
<option value="weekly">Wöchentlich</option>
|
||||
<option value="monthly">Monatlich</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="enddatum-group" style="display: none;">
|
||||
<label for="enddatum">Wiederholen bis *</label>
|
||||
<input type="date" id="enddatum" name="enddatum">
|
||||
</div>
|
||||
<button type="submit" class="btn-submit-vergabe">Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="form-container">
|
||||
<h3>📋 Aktuelle Terminbelegungen</h3>
|
||||
<div class="table-container">
|
||||
<div id="termineTableWrapper">Lade Daten...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script src="/js/AjaxRenderTables.js"></script>
|
||||
<script src="/js/ajaxApi/TermineAjaxMethods.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const statusMsg = document.getElementById('statusMsg');
|
||||
const tableWrapper = document.getElementById('termineTableWrapper');
|
||||
|
||||
const intervallNamen = {
|
||||
daily: 'Täglich',
|
||||
weekly: 'Wöchentlich',
|
||||
monthly: 'Monatlich'
|
||||
};
|
||||
|
||||
function zeigeStatus(text, istFehler = false) {
|
||||
statusMsg.textContent = text;
|
||||
statusMsg.style.display = 'block';
|
||||
statusMsg.style.background = istFehler ? '#f8d7da' : '';
|
||||
statusMsg.style.color = istFehler ? '#721c24' : '';
|
||||
setTimeout(() => {
|
||||
statusMsg.style.display = 'none';
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
function formatDatum(iso) {
|
||||
const d = new Date(iso);
|
||||
return d.toLocaleDateString('de-DE');
|
||||
}
|
||||
|
||||
function formatZeit(zeit) {
|
||||
return zeit?.slice(0, 5) ?? '';
|
||||
}
|
||||
|
||||
function renderTermine(termine) {
|
||||
if (!termine || termine.length === 0) {
|
||||
tableWrapper.innerHTML = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead><tr><th>Datum / Turnus</th><th>Uhrzeit</th><th>Termin / Name</th><th>Notiz</th><th>Eingetragen von</th><th>Aktion</th></tr></thead>
|
||||
<tbody><tr><td colspan="6" style="text-align:center; padding:20px;">Keine Termine eingetragen.</td></tr></tbody>
|
||||
</table>`;
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr><th>Datum / Turnus</th><th>Uhrzeit</th><th>Termin / Name</th><th>Notiz</th><th>Eingetragen von</th><th>Aktion</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
for (const t of termine) {
|
||||
const istSerie = !!t.SERIEN_ID;
|
||||
html += `
|
||||
<tr>
|
||||
<td data-label="Datum:">
|
||||
<strong>${escapeHtml(formatDatum(t.DATUM))}</strong>
|
||||
${istSerie ? `<br><small style="color:#007bff; font-style:italic;">(${escapeHtml(intervallNamen[t.INTERVALL] ?? '')} bis ${escapeHtml(formatDatum(t.SERIEN_ENDE))})</small>` : ''}
|
||||
</td>
|
||||
<td data-label="Uhrzeit:">${escapeHtml(formatZeit(t.ZEIT))}</td>
|
||||
<td data-label="Termin / Name:">
|
||||
<strong>${escapeHtml(t.NAME)}</strong>
|
||||
${istSerie ? `<span style="font-size:0.8em; background:#e1f5fe; color:#0288d1; padding:2px 6px; border-radius:10px; margin-left:5px;">Wiederholend</span>` : ''}
|
||||
</td>
|
||||
<td data-label="Notiz:">${escapeHtml(t.NOTIZ || '-')}</td>
|
||||
<td data-label="Von:"><span class="badge">${escapeHtml(t.ERSTELLT_VON)}</span></td>
|
||||
<td data-label="Aktion:">
|
||||
${istSerie
|
||||
? `<button type="button" class="btn-delete btn-delete-serie" data-serien-id="${escapeHtml(t.SERIEN_ID)}" style="background-color:#d32f2f;">löschen</button>`
|
||||
: `<button type="button" class="btn-delete btn-delete-einzeln" data-id="${escapeHtml(t.ID)}">Löschen</button>`
|
||||
}
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
html += `</tbody></table>`;
|
||||
tableWrapper.innerHTML = html;
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-delete-einzeln').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du diesen Termin wirklich löschen?')) return;
|
||||
try {
|
||||
const data = await deleteTermin(btn.dataset.id);
|
||||
zeigeStatus(data.message);
|
||||
await ladeTermine();
|
||||
} catch (err) {
|
||||
zeigeStatus('Fehler: ' + err.message, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-delete-serie').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du die GESAMTE Terminserie löschen?')) return;
|
||||
try {
|
||||
const data = await deleteTerminSerie(btn.dataset.serienId);
|
||||
zeigeStatus(data.message);
|
||||
await ladeTermine();
|
||||
} catch (err) {
|
||||
zeigeStatus('Fehler: ' + err.message, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function ladeTermine() {
|
||||
try {
|
||||
const data = await getTermine();
|
||||
renderTermine(data.entries);
|
||||
} catch (err) {
|
||||
tableWrapper.innerHTML = `<p>❌ ${escapeHtml(err.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Wiederholung: Enddatum-Feld ein-/ausblenden
|
||||
// -----------------------------------------------------------
|
||||
document.getElementById('wiederholung').addEventListener('change', function() {
|
||||
const enddatumGroup = document.getElementById('enddatum-group');
|
||||
const enddatumInput = document.getElementById('enddatum');
|
||||
|
||||
if (this.value !== 'none') {
|
||||
enddatumGroup.style.display = 'block';
|
||||
enddatumInput.required = true;
|
||||
} else {
|
||||
enddatumGroup.style.display = 'none';
|
||||
enddatumInput.required = false;
|
||||
enddatumInput.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Formular absenden
|
||||
// -----------------------------------------------------------
|
||||
document.getElementById('addTerminForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const datum = document.getElementById('datum').value;
|
||||
const zeit = document.getElementById('zeit').value;
|
||||
const name = document.getElementById('name').value.trim();
|
||||
const notiz = document.getElementById('notiz').value.trim();
|
||||
const wiederholung = document.getElementById('wiederholung').value;
|
||||
const enddatum = document.getElementById('enddatum').value;
|
||||
|
||||
try {
|
||||
const data = await addTermin(datum, zeit, name, notiz, wiederholung, enddatum);
|
||||
zeigeStatus(data.message);
|
||||
e.target.reset();
|
||||
document.getElementById('enddatum-group').style.display = 'none';
|
||||
await ladeTermine();
|
||||
} catch (err) {
|
||||
zeigeStatus('Fehler: ' + err.message, true);
|
||||
}
|
||||
});
|
||||
|
||||
ladeTermine();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,144 @@
|
||||
<div class="arbeitsberichte-wrapper">
|
||||
<div class="vergabe-form">
|
||||
<h3>Neues Ticket anlegen</h3>
|
||||
<form id="addTicketForm" class="vergabe-grid">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Titel</label>
|
||||
<input type="text" name="titel" id="in-titel" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Beschreibung</label>
|
||||
<input type="text" name="beschreibung" id="in-beschreibung" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit-vergabe">💾 Speichern</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-container" style="margin-top: 30px;">
|
||||
<h3>Tickets</h3>
|
||||
<div id="tableWrapper">
|
||||
<p>Lade Daten...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script src="/js/ajaxApi/TicketBoardAjaxMethods.js"></script>
|
||||
<script src="/js/AjaxRenderTables.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tableWrapper = document.getElementById('tableWrapper');
|
||||
const POLL_INTERVAL_MS = 5000;
|
||||
let lastStatusById = {};
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Tabelle rendern (eigene Render-Funktion wg. Status-Highlight)
|
||||
// -----------------------------------------------------------
|
||||
function renderTicketsTabelle(entries) {
|
||||
if (!entries || entries.length === 0) {
|
||||
tableWrapper.innerHTML = '<p>Keine Tickets vorhanden.</p>';
|
||||
lastStatusById = {};
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Titel</th><th>Beschreibung</th><th>Status</th>
|
||||
<th style="width: 100px;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
for (const e of entries) {
|
||||
const changed = lastStatusById[e.ID] !== undefined &&
|
||||
lastStatusById[e.ID] !== e.STATUS;
|
||||
|
||||
html += `
|
||||
<tr data-id="${escapeHtml(e.ID)}" class="${changed ? 'status-row-changed' : ''}">
|
||||
<td data-label="Titel">${escapeHtml(e.TITEL)}</td>
|
||||
<td data-label="Beschreibung">${escapeHtml(e.BESCHREIBUNG)}</td>
|
||||
<td data-label="Status"><span class="status-badge">${escapeHtml(e.STATUS)}</span></td>
|
||||
<td data-label="Aktion">
|
||||
<button type="button" class="btn-delete" data-id="${escapeHtml(e.ID)}">Löschen</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
html += `</tbody></table>`;
|
||||
tableWrapper.innerHTML = html;
|
||||
|
||||
const neueStatus = {};
|
||||
entries.forEach(e => {
|
||||
neueStatus[e.ID] = e.STATUS;
|
||||
});
|
||||
lastStatusById = neueStatus;
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du dieses Ticket wirklich löschen?')) return;
|
||||
try {
|
||||
await deleteTicketEntry(btn.dataset.id);
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Löschen: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function ladeListe() {
|
||||
try {
|
||||
const data = await getTicketEntries();
|
||||
renderTicketsTabelle(data.entries);
|
||||
} catch (err) {
|
||||
tableWrapper.innerHTML = `<p>❌ ${escapeHtml(err.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Ticket anlegen
|
||||
// -----------------------------------------------------------
|
||||
document.getElementById('addTicketForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const titel = document.getElementById('in-titel').value.trim();
|
||||
const beschreibung = document.getElementById('in-beschreibung').value.trim();
|
||||
|
||||
if (!titel || !beschreibung) return;
|
||||
|
||||
try {
|
||||
await addTicketEntry(titel, beschreibung);
|
||||
e.target.reset();
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Speichern: ' + err.message);
|
||||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Initialisierung + zyklische Aktualisierung
|
||||
// -----------------------------------------------------------
|
||||
ladeListe();
|
||||
setInterval(ladeListe, POLL_INTERVAL_MS);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@keyframes statusChanged {
|
||||
0% {
|
||||
background-color: #fff3cd;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.status-row-changed {
|
||||
animation: statusChanged 2.5s ease-out;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<div class="arbeitsberichte-wrapper">
|
||||
<div class="table-container" style="margin-top: 30px;">
|
||||
<h3>Ticket Verwaltung</h3>
|
||||
<div id="tableWrapper">
|
||||
<p>Lade Daten...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script src="/js/AjaxRenderTables.js"></script>
|
||||
<script src="/js/ajaxApi/TicketControlAjaxMethods.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tableWrapper = document.getElementById('tableWrapper');
|
||||
const allowedStatuses = ['OFFEN', 'DEPLOYED', 'ABGELEHNT', 'ANGENOMMEN'];
|
||||
|
||||
function renderTicketControTabelle(entries) {
|
||||
if (!entries || entries.length === 0) {
|
||||
tableWrapper.innerHTML = '<p>Keine Tickets vorhanden.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Titel</th><th>Beschreibung</th><th>Status</th><th>Ersteller</th>
|
||||
<th style="width: 220px;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
for (const e of entries) {
|
||||
const options = allowedStatuses.map(s =>
|
||||
`<option value="${escapeHtml(s)}" ${e.STATUS === s ? 'selected' : ''}>${escapeHtml(s)}</option>`
|
||||
).join('');
|
||||
|
||||
html += `
|
||||
<tr data-id="${escapeHtml(e.ID)}">
|
||||
<td data-label="Titel">${escapeHtml(e.TITEL)}</td>
|
||||
<td data-label="Beschreibung">${escapeHtml(e.BESCHREIBUNG)}</td>
|
||||
<td data-label="Status">${escapeHtml(e.STATUS)}</td>
|
||||
<td data-label="Ersteller">${escapeHtml(e.ERSTELLER)}</td>
|
||||
<td data-label="Aktion">
|
||||
<div style="display:flex; flex-direction:column; gap:6px; align-items:stretch;">
|
||||
<select class="status-select" data-id="${escapeHtml(e.ID)}">
|
||||
${options}
|
||||
</select>
|
||||
<button type="button" class="btn-edit" data-id="${escapeHtml(e.ID)}">Speichern</button>
|
||||
<button type="button" class="btn-delete" data-id="${escapeHtml(e.ID)}" style="margin-top:3pt;">Löschen</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
html += `</tbody></table>`;
|
||||
tableWrapper.innerHTML = html;
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-edit').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const id = btn.dataset.id;
|
||||
const select = tableWrapper.querySelector(
|
||||
`.status-select[data-id="${id}"]`);
|
||||
try {
|
||||
await editTicketControEntry(id, select.value);
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Speichern: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du dieses Ticket wirklich löschen?')) return;
|
||||
try {
|
||||
await deleteTicketControEntry(btn.dataset.id);
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Löschen: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function ladeListe() {
|
||||
try {
|
||||
const data = await getTicketControEntries();
|
||||
renderTicketControTabelle(data.entries);
|
||||
} catch (err) {
|
||||
tableWrapper.innerHTML = `<p>❌ ${escapeHtml(err.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
ladeListe();
|
||||
});
|
||||
</script>
|
||||
@@ -1,10 +1,78 @@
|
||||
<?php
|
||||
// 1. Logik zum Verarbeiten des Formulars (bevor die Liste geladen wird)
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_user'])) {
|
||||
// Nur Admins dürfen Benutzer hinzufügen (Sicherheitscheck)
|
||||
if ((new getSpecificData())->getIsAdmin()) {
|
||||
$controller = new UserController();
|
||||
$controller->setUser(
|
||||
$_POST['name'],
|
||||
$_POST['nachname'],
|
||||
$_POST['email'],
|
||||
$_POST['passwort'],
|
||||
$_POST['rolle'],
|
||||
$_POST['admin'] ?? 0, // Falls Checkbox nicht gesetzt, 0 senden
|
||||
$_POST['home_ip']
|
||||
);
|
||||
// Optional: Seite neu laden, um die Liste zu aktualisieren
|
||||
header("Location: " . (new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['password'])) {
|
||||
if (isset($superuser['admin']['password']) && $_POST['password'] === $superuser['admin']['password']) {
|
||||
$_SESSION['logged_in'] = true;
|
||||
} else {
|
||||
$error = "Falsches Passwort!";
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Daten für die Tabelle holen (dein bestehender Code)
|
||||
$specificData = new getSpecificData();
|
||||
$isAdmin = $specificData->getIsAdmin();
|
||||
|
||||
if ($isAdmin) {
|
||||
$users = DatabaseShow("Benutzer", "NAME, NACHNAME, EMAIL, ROLLE, ADMIN, HOME_IP");
|
||||
} else {
|
||||
$users = DatabaseShow(
|
||||
"Benutzer",
|
||||
"NAME, NACHNAME, EMAIL, ROLLE, ADMIN, HOME_IP",
|
||||
"NAME = ?",
|
||||
$specificData->getUsername()
|
||||
);
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_user'])) {
|
||||
|
||||
if ((new getSpecificData())->getIsAdmin()) {
|
||||
|
||||
$controller = new UserController();
|
||||
$controller->DeleteUser(
|
||||
$_POST['delete_name'],
|
||||
$_POST['delete_nachname'],
|
||||
$_POST['delete_email']
|
||||
);
|
||||
|
||||
header("Location: " . (new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['logout'])) {
|
||||
unset($_SESSION['logged_in']);
|
||||
header("Location: " . (new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<div class="content">
|
||||
<?php if (!$loggedIn): ?>
|
||||
<?php if (!($_SESSION['logged_in'] ?? false)): ?>
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<form method="post">
|
||||
<h2>🔐 Login</h2>
|
||||
<?php if($error): ?>
|
||||
<?php if(isset($error)): ?>
|
||||
<div class="status-msg" style="background:#dc3545; color:#fff;"><?= htmlspecialchars($error) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
@@ -88,3 +156,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
+27
-1
@@ -1,3 +1,29 @@
|
||||
<?php
|
||||
if (isset($_POST['toggle_variable'])) {
|
||||
|
||||
$name = $_POST['variablenname'];
|
||||
|
||||
$current = (new getSpecificData())->getLoggingStatus($name)[0]['WERT'];
|
||||
$newValue = ($current === "ON") ? "OFF" : "ON";
|
||||
|
||||
DatabaseUpdate(
|
||||
"Variablen",
|
||||
"WERT = ?",
|
||||
"VARIABLENNAME = ?",
|
||||
$newValue,
|
||||
$name
|
||||
);
|
||||
header("Location: " . (new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
$variablen = DatabaseShow(
|
||||
"Variablen",
|
||||
"VARIABLENNAME, WERT, ANZEIGENAME"
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
<div class="admin-grid">
|
||||
|
||||
<?php foreach ($variablen as $var): ?>
|
||||
@@ -26,4 +52,4 @@
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,284 @@
|
||||
<div class="arbeitsberichte-wrapper">
|
||||
<div class="vergabe-form">
|
||||
<form id="addForm" class="vergabe-grid">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Wann ausgeliehen</label>
|
||||
<input type="date" name="wann" id="in-wann" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Rückgabe</label>
|
||||
<input type="date" name="rueckgabe" id="in-rueckgabe" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group autocomplete-wrapper">
|
||||
<label>An wen</label>
|
||||
<input type="text" id="wer-input" placeholder="Name eingeben" required autocomplete="off">
|
||||
<div class="autocomplete-suggestions" id="wer-suggestions"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group autocomplete-wrapper">
|
||||
<label>Was</label>
|
||||
<input type="text" id="was-input" placeholder="Gegenstand eingeben" required autocomplete="off">
|
||||
<div class="autocomplete-suggestions" id="was-suggestions"></div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit-vergabe">Speichern</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="vergabe-form" style="margin-top: 30px;">
|
||||
<form id="searchForm" class="vergabe-grid">
|
||||
|
||||
<div class="form-group autocomplete-wrapper">
|
||||
<label for="search-wer-input">Nach Person filtern</label>
|
||||
<input type="text" id="search-wer-input" placeholder="Name eingeben..." autocomplete="off">
|
||||
<div class="autocomplete-suggestions" id="search-wer-suggestions"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group autocomplete-wrapper">
|
||||
<label for="search-was-input">Nach Gegenstand filtern</label>
|
||||
<input type="text" id="search-was-input" placeholder="Gegenstand eingeben..." autocomplete="off">
|
||||
<div class="autocomplete-suggestions" id="search-was-suggestions"></div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 10px; align-items: flex-end;">
|
||||
<button type="submit" class="btn-submit-vergabe" style="margin: 0; flex-grow: 1;">Suchen</button>
|
||||
<button type="button" id="resetSearch" class="btn-delete" style="margin: 0;">Zurücksetzen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-container" style="margin-top: 30px;">
|
||||
<h3 id="tableHeading">Aktuelle Vergaben</h3>
|
||||
<div id="tableWrapper">
|
||||
<p>Lade Daten...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script src="/js/ajaxApi/VergabeAjaxMethods.js"></script>
|
||||
<script src="/js/AjaxRenderTables.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tableWrapper = document.getElementById('tableWrapper');
|
||||
const tableHeading = document.getElementById('tableHeading');
|
||||
|
||||
let aktuellSearchWer = '';
|
||||
let aktuellSearchWas = '';
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Tabelle rendern (eigene Render-Funktion, da "Zurückgeben"
|
||||
// eine andere Aktion ist als das generische Löschen)
|
||||
// -----------------------------------------------------------
|
||||
function renderVergabeTabelle(entries) {
|
||||
if (!entries || entries.length === 0) {
|
||||
tableWrapper.innerHTML = '<p>Keine Einträge vorhanden.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Was</th><th>Wer</th><th>Wann ausgeliehen</th><th>Rückgabe</th>
|
||||
<th style="width: 100px;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
for (const e of entries) {
|
||||
html += `
|
||||
<tr data-id="${escapeHtml(e.ID)}">
|
||||
<td data-label="Was">${escapeHtml(e.WAS)}</td>
|
||||
<td data-label="Wer">${escapeHtml(e.WER)}</td>
|
||||
<td data-label="Wann">${escapeHtml(e.WANN)}</td>
|
||||
<td data-label="Rückgabe">${escapeHtml(e.RUECKGABE)}</td>
|
||||
<td data-label="Aktion">
|
||||
<button type="button" class="btn-delete" data-id="${escapeHtml(e.ID)}">Löschen</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
html += `</tbody></table>`;
|
||||
tableWrapper.innerHTML = html;
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du diesen Eintrag wirklich löschen?')) return;
|
||||
try {
|
||||
await returnVergabeEntry(btn.dataset.id);
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Löschen: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function aktualisiereUeberschrift() {
|
||||
if (aktuellSearchWer && aktuellSearchWas) {
|
||||
tableHeading.textContent = `Gefiltert nach: ${aktuellSearchWer} ➔ ${aktuellSearchWas}`;
|
||||
} else if (aktuellSearchWer) {
|
||||
tableHeading.textContent = `Vergaben für: ${aktuellSearchWer}`;
|
||||
} else if (aktuellSearchWas) {
|
||||
tableHeading.textContent = `Ausgeliehene Gegenstände: ${aktuellSearchWas}`;
|
||||
} else {
|
||||
tableHeading.textContent = 'Aktuelle Vergaben';
|
||||
}
|
||||
}
|
||||
|
||||
async function ladeListe() {
|
||||
try {
|
||||
const data = await getVergabeEntries(aktuellSearchWer, aktuellSearchWas);
|
||||
aktualisiereUeberschrift();
|
||||
renderVergabeTabelle(data.entries);
|
||||
} catch (err) {
|
||||
tableWrapper.innerHTML = `<p>❌ ${escapeHtml(err.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Eintrag hinzufügen
|
||||
// -----------------------------------------------------------
|
||||
document.getElementById('addForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const wann = document.getElementById('in-wann').value;
|
||||
const rueckgabe = document.getElementById('in-rueckgabe').value;
|
||||
const wer = document.getElementById('wer-input').value.trim();
|
||||
const was = document.getElementById('was-input').value.trim();
|
||||
|
||||
if (!wann || !rueckgabe || !wer || !was) return;
|
||||
|
||||
try {
|
||||
await addVergabeEntry(was, wer, wann, rueckgabe);
|
||||
e.target.reset();
|
||||
document.getElementById('in-wann').value = new Date().toISOString().slice(0, 10);
|
||||
document.getElementById('in-rueckgabe').value = new Date().toISOString().slice(0, 10);
|
||||
await ladeVorschlaege(); // neue WAS/WER-Werte könnten dazugekommen sein
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Speichern: ' + err.message);
|
||||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Suche
|
||||
// -----------------------------------------------------------
|
||||
document.getElementById('searchForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
aktuellSearchWer = document.getElementById('search-wer-input').value.trim();
|
||||
aktuellSearchWas = document.getElementById('search-was-input').value.trim();
|
||||
await ladeListe();
|
||||
});
|
||||
|
||||
document.getElementById('resetSearch').addEventListener('click', async () => {
|
||||
aktuellSearchWer = '';
|
||||
aktuellSearchWas = '';
|
||||
document.getElementById('search-wer-input').value = '';
|
||||
document.getElementById('search-was-input').value = '';
|
||||
await ladeListe();
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Autocomplete (unverändert zu deiner Logik, nur Datenquelle
|
||||
// kommt jetzt per AJAX statt server-seitig eingebettet)
|
||||
// -----------------------------------------------------------
|
||||
function setupAutocomplete(inputId, suggestionsId, data, submitOnSelect = false) {
|
||||
const input = document.getElementById(inputId);
|
||||
const box = document.getElementById(suggestionsId);
|
||||
if (!input || !box) return;
|
||||
|
||||
let items = [];
|
||||
let activeIndex = -1;
|
||||
|
||||
function render(list) {
|
||||
box.innerHTML = '';
|
||||
items = list;
|
||||
activeIndex = -1;
|
||||
if (list.length === 0) {
|
||||
box.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
list.forEach((val, i) => {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = val;
|
||||
div.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
selectItem(i);
|
||||
});
|
||||
box.appendChild(div);
|
||||
});
|
||||
box.style.display = 'block';
|
||||
}
|
||||
|
||||
function updateActive() {
|
||||
[...box.children].forEach((el, i) => el.classList.toggle('active', i === activeIndex));
|
||||
if (activeIndex >= 0) box.children[activeIndex].scrollIntoView({
|
||||
block: 'nearest'
|
||||
});
|
||||
}
|
||||
|
||||
function selectItem(i) {
|
||||
if (items[i] !== undefined) input.value = items[i];
|
||||
box.style.display = 'none';
|
||||
activeIndex = -1;
|
||||
if (submitOnSelect) {
|
||||
input.closest('form').dispatchEvent(new Event('submit', {
|
||||
cancelable: true
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
const val = input.value.toLowerCase();
|
||||
if (val.length === 0) {
|
||||
box.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
render(data.filter(d => d.toLowerCase().includes(val)));
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (box.style.display !== 'block') return;
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
activeIndex = Math.min(activeIndex + 1, items.length - 1);
|
||||
updateActive();
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
activeIndex = Math.max(activeIndex - 1, 0);
|
||||
updateActive();
|
||||
} else if (e.key === 'Tab' || e.key === 'Enter') {
|
||||
if (activeIndex >= 0) {
|
||||
e.preventDefault();
|
||||
selectItem(activeIndex);
|
||||
} else box.style.display = 'none';
|
||||
} else if (e.key === 'Escape') box.style.display = 'none';
|
||||
});
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target !== input) box.style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
async function ladeVorschlaege() {
|
||||
const data = await getVergabeSuggestions();
|
||||
setupAutocomplete('was-input', 'was-suggestions', data.was);
|
||||
setupAutocomplete('wer-input', 'wer-suggestions', data.wer);
|
||||
setupAutocomplete('search-wer-input', 'search-wer-suggestions', data.wer, true);
|
||||
setupAutocomplete('search-was-input', 'search-was-suggestions', data.was, true);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Initialisierung
|
||||
// -----------------------------------------------------------
|
||||
document.getElementById('in-wann').value = new Date().toISOString().slice(0, 10);
|
||||
document.getElementById('in-rueckgabe').value = new Date().toISOString().slice(0, 10);
|
||||
|
||||
ladeVorschlaege();
|
||||
ladeListe();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
$conn = db_connect();
|
||||
|
||||
$sql = "SELECT ZEIT, AKTUELL FROM Wetter";
|
||||
$result = $conn->query($sql);
|
||||
|
||||
$labels = [];
|
||||
$data = [];
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
while($row = $result->fetch_assoc()) {
|
||||
$labels[] = $row['ZEIT'];
|
||||
$data[] = (float)$row['AKTUELL'];
|
||||
}
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<canvas id="logChart" width="600" height="400"></canvas>
|
||||
|
||||
<script>
|
||||
const ctx = document.getElementById('logChart').getContext('2d');
|
||||
const logChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: <?php echo json_encode($labels); ?>,
|
||||
datasets: [{
|
||||
label: 'Wetter',
|
||||
data: <?php echo json_encode($data); ?>,
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
fill: false,
|
||||
tension: 0
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
zoom: {
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'x',
|
||||
},
|
||||
zoom: {
|
||||
wheel: {
|
||||
enabled: true,
|
||||
},
|
||||
mode: 'x',
|
||||
}
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Zeitraum'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
min: -10,
|
||||
max: 40,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Temperatur in °C'
|
||||
},
|
||||
ticks: {
|
||||
stepSize: 1
|
||||
},
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,88 @@
|
||||
<div class="arbeitsberichte-wrapper">
|
||||
<div id="overtimeCard"
|
||||
style="background: rgba(150,150,150,0.05); padding: 25px; border-radius: 10px; margin-bottom: 25px; border: 1px solid rgba(150,150,150,0.2); border-left: 6px solid #007bff;">
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<span style="color: #888; font-size: 0.8rem; font-weight: bold; text-transform: uppercase;">Aktueller
|
||||
Stand</span>
|
||||
<h2 id="standAnzeige" style="margin: 5px 0 0 0; font-size: 2rem;">Lädt...</h2>
|
||||
</div>
|
||||
|
||||
<form id="overtimeForm" style="display: none; gap: 15px; flex-wrap: wrap; align-items: flex-end;">
|
||||
<div style="display: flex; flex-direction: column; gap: 5px;">
|
||||
<label style="font-size: 0.85rem; font-weight: 600; color: inherit;">Datum</label>
|
||||
<input type="date" id="ot-date" required
|
||||
style="padding: 8px; border: 1px solid rgba(150,150,150,0.3); border-radius: 4px; background: rgba(150,150,150,0.1); color: inherit;">
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; gap: 5px;">
|
||||
<label style="font-size: 0.85rem; font-weight: 600; color: inherit;">Minuten</label>
|
||||
<input type="number" id="ot-minutes" placeholder="480" required
|
||||
style="width: 100px; padding: 8px; border: 1px solid rgba(150,150,150,0.3); border-radius: 4px; background: rgba(150,150,150,0.1); color: inherit;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label style="font-size: 0.85rem; font-weight: 600; color: inherit;">Grund</label>
|
||||
<select id="ot-reason" required>
|
||||
<option value="">Bitte wählen</option>
|
||||
<option value="Abbau">Abbau</option>
|
||||
<option value="Aufbau">Aufbau</option>
|
||||
<option value="Anderes">Anderes</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit"
|
||||
style="background: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; font-weight: bold; cursor: pointer;">
|
||||
Eintrag speichern
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script src="/js/AjaxRenderTables.js"></script>
|
||||
<script src="/js/ajaxApi/ZeiterfassungAjaxMethods.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const standAnzeige = document.getElementById('standAnzeige');
|
||||
const overtimeForm = document.getElementById('overtimeForm');
|
||||
|
||||
function formatStand(gesamtMinuten) {
|
||||
const stunden = Math.trunc(gesamtMinuten / 60);
|
||||
const minuten = Math.abs(gesamtMinuten % 60);
|
||||
return `${stunden}h ${minuten}min`;
|
||||
}
|
||||
|
||||
async function ladeStand() {
|
||||
try {
|
||||
const data = await getOvertimeStats();
|
||||
|
||||
standAnzeige.textContent = formatStand(data.gesamtMinuten);
|
||||
standAnzeige.style.color = data.gesamtMinuten >= 0 ? '#28a745' : '#dc3545';
|
||||
|
||||
overtimeForm.style.display = data.istBerechtigt ? 'flex' : 'none';
|
||||
|
||||
} catch (err) {
|
||||
standAnzeige.textContent = 'Fehler beim Laden';
|
||||
console.error(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
overtimeForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const datum = document.getElementById('ot-date').value;
|
||||
const minuten = document.getElementById('ot-minutes').value;
|
||||
const grund = document.getElementById('ot-reason').value;
|
||||
|
||||
try {
|
||||
await addOvertimeEntry(datum, minuten, grund);
|
||||
e.target.reset();
|
||||
document.getElementById('ot-date').value = new Date().toISOString().slice(0, 10);
|
||||
await ladeStand();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Speichern: ' + err.message);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('ot-date').value = new Date().toISOString().slice(0, 10);
|
||||
ladeStand();
|
||||
});
|
||||
</script>
|
||||
@@ -1,227 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\User;
|
||||
use MVC\Model\Variable;
|
||||
|
||||
class AdminController extends Controller {
|
||||
public function user_control() {
|
||||
global $isAdmin, $superuser;
|
||||
$specificData = new \getSpecificData();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['add_user']) && $isAdmin) {
|
||||
User::insert([
|
||||
'NAME' => $_POST['name'],
|
||||
'NACHNAME' => $_POST['nachname'],
|
||||
'EMAIL' => $_POST['email'],
|
||||
'PASSWORD' => password_hash($_POST['passwort'], PASSWORD_DEFAULT),
|
||||
'ROLLE' => $_POST['rolle'],
|
||||
'ADMIN' => $_POST['admin'] ?? 0,
|
||||
'HOME_IP' => $_POST['home_ip']
|
||||
]);
|
||||
header("Location: " . $specificData->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_POST['delete_user']) && $isAdmin) {
|
||||
User::delete("NAME = ? AND NACHNAME = ? AND EMAIL = ?", $_POST['delete_name'], $_POST['delete_nachname'], $_POST['delete_email']);
|
||||
header("Location: " . $specificData->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_POST['password'])) {
|
||||
if (isset($superuser['admin']['password']) && $_POST['password'] === $superuser['admin']['password']) {
|
||||
$_SESSION['logged_in'] = true;
|
||||
} else {
|
||||
$error = "Falsches Passwort!";
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['logout'])) {
|
||||
unset($_SESSION['logged_in']);
|
||||
header("Location: " . $specificData->getReferer());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$users = $isAdmin ? User::all() : User::getByName($specificData->getUsername());
|
||||
|
||||
return $this->render('user_control', [
|
||||
'users' => $users,
|
||||
'isAdmin' => $isAdmin,
|
||||
'error' => $error ?? null,
|
||||
'loggedIn' => $_SESSION['logged_in'] ?? false
|
||||
]);
|
||||
}
|
||||
|
||||
public function variablen_control() {
|
||||
global $isAdmin;
|
||||
if (!$isAdmin) return "Access Denied";
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['toggle_variable'])) {
|
||||
Variable::toggle($_POST['variablenname']);
|
||||
header("Location: " . (new \getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
return $this->render('variablen_control', ['variablen' => Variable::all()]);
|
||||
}
|
||||
|
||||
public function services_control() {
|
||||
global $isAdmin, $superuser, $serviceGroups, $services, $linkGroups, $linkButtons;
|
||||
if (!$isAdmin) return "Access Denied";
|
||||
|
||||
$configFile = 'config.php';
|
||||
$backupDir = 'config_backups';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['password'])) {
|
||||
if (isset($superuser['admin']['password']) && $_POST['password'] === $superuser['admin']['password']) {
|
||||
$_SESSION['logged_in'] = true;
|
||||
} else {
|
||||
$error = "Falsches Passwort!";
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['logout'])) {
|
||||
unset($_SESSION['logged_in']);
|
||||
header("Location: " . (new \getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_POST['save']) && ($_SESSION['logged_in'] ?? false)) {
|
||||
if (!is_dir($backupDir)) mkdir($backupDir, 0755, true);
|
||||
copy($configFile, $backupDir . '/config_' . date('Y-m-d_H-i-s') . '.php');
|
||||
|
||||
$newServices = [];
|
||||
$serviceToGroupMapping = [];
|
||||
if(isset($_POST['services'])){
|
||||
foreach ($_POST['services'] as $id => $s) {
|
||||
$finalId = !empty($s['new_id']) ? trim($s['new_id']) : $id;
|
||||
if (empty($finalId)) continue;
|
||||
$newServices[$finalId] = [
|
||||
'name' => $s['name'],
|
||||
'active' => isset($s['active']),
|
||||
'adminOnly' => isset($s['adminOnly'])
|
||||
];
|
||||
if(!empty($s['targetGroup'])) {
|
||||
$serviceToGroupMapping[$s['targetGroup']][] = $finalId;
|
||||
}
|
||||
}
|
||||
(new \RightsController())->RightsHandler($s['name']);
|
||||
}
|
||||
|
||||
$newServiceGroups = [];
|
||||
if(isset($_POST['serviceGroups'])){
|
||||
foreach ($_POST['serviceGroups'] as $id => $groupData) {
|
||||
$finalId = !empty($groupData['new_id']) ? trim($groupData['new_id']) : $id;
|
||||
if (empty($finalId)) continue;
|
||||
$currentServices = $serviceToGroupMapping[$finalId] ?? ($serviceGroups[$id]['services'] ?? []);
|
||||
$newServiceGroups[$finalId] = [
|
||||
'label' => $groupData['label'],
|
||||
'defaultOpen' => isset($groupData['defaultOpen']),
|
||||
'services' => array_values(array_unique($currentServices))
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$newLinkGroups = [];
|
||||
if(isset($_POST['linkGroups'])){
|
||||
foreach ($_POST['linkGroups'] as $id => $lg) {
|
||||
$finalId = !empty($lg['new_id']) ? trim($lg['new_id']) : $id;
|
||||
if (empty($finalId)) continue;
|
||||
$newLinkGroups[$finalId] = [
|
||||
'label' => $lg['label'],
|
||||
'color' => $lg['color'],
|
||||
'defaultOpen' => isset($lg['defaultOpen'])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$newLinkButtons = [];
|
||||
if(isset($_POST['linkButtons'])){
|
||||
foreach ($_POST['linkButtons'] as $btn) {
|
||||
if (empty($btn['label'])) continue;
|
||||
$newLinkButtons[] = [
|
||||
'label' => $btn['label'],
|
||||
'url' => $btn['url'],
|
||||
'newTab' => isset($btn['newTab']),
|
||||
'adminOnly' => isset($btn['adminOnly']),
|
||||
'group' => $btn['group']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$content = "<?php\n\n";
|
||||
$content .= '$superuser = ' . var_export($superuser, true) . ";\n\n";
|
||||
$content .= '$serviceGroups = ' . var_export($newServiceGroups, true) . ";\n\n";
|
||||
$content .= '$services = ' . var_export($newServices, true) . ";\n\n";
|
||||
$content .= '$linkGroups = ' . var_export($newLinkGroups, true) . ";\n\n";
|
||||
$content .= '$linkButtons = ' . var_export($newLinkButtons, true) . ";\n";
|
||||
|
||||
if (file_put_contents($configFile, $content)) {
|
||||
if (function_exists('opcache_invalidate')) { @opcache_invalidate($configFile, true); }
|
||||
$_SESSION['status_message'] = "✅ Konfiguration erfolgreich aktualisiert!";
|
||||
header("Location: " . (new \getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$message = $_SESSION['status_message'] ?? null;
|
||||
unset($_SESSION['status_message']);
|
||||
|
||||
return $this->render('services_control', [
|
||||
'serviceGroups' => $serviceGroups,
|
||||
'services' => $services,
|
||||
'linkGroups' => $linkGroups,
|
||||
'linkButtons' => $linkButtons,
|
||||
'loggedIn' => $_SESSION['logged_in'] ?? false,
|
||||
'error' => $error ?? null,
|
||||
'message' => $message
|
||||
]);
|
||||
}
|
||||
|
||||
public function data_info() {
|
||||
global $isAdmin;
|
||||
if (!$isAdmin) return "Access Denied";
|
||||
|
||||
$data = new \getSpecificData();
|
||||
$allData = $data->getAllData();
|
||||
|
||||
$groupedData = [];
|
||||
foreach ($allData as $key => $value) {
|
||||
if (strpos($key, ' - ') !== false) {
|
||||
[$group, $field] = explode(' - ', $key, 2);
|
||||
} else {
|
||||
$group = 'Other';
|
||||
$field = $key;
|
||||
}
|
||||
$groupedData[$group][$field] = $value;
|
||||
}
|
||||
|
||||
return $this->render('data_info', ['groupedData' => $groupedData]);
|
||||
}
|
||||
|
||||
public function tabellen() {
|
||||
global $isAdmin;
|
||||
if (!$isAdmin) return "Access Denied";
|
||||
|
||||
$tables = \DatabaseShowTables();
|
||||
$selectedTable = $_GET['table'] ?? null;
|
||||
$structure = [];
|
||||
|
||||
if ($selectedTable && in_array($selectedTable, $tables)) {
|
||||
$structure = \DatabaseShowTableStructure($selectedTable);
|
||||
}
|
||||
|
||||
return $this->render('tabellen', [
|
||||
'tables' => $tables,
|
||||
'selectedTable' => $selectedTable,
|
||||
'structure' => $structure
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
|
||||
class ApiController extends Controller {
|
||||
public function api_tester() {
|
||||
$apiDir = realpath(__DIR__ . '/../../../api');
|
||||
$apiOptions = [];
|
||||
|
||||
if ($apiDir !== false) {
|
||||
$iterator = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($apiDir, \RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if (!$file->isFile() || $file->getExtension() !== 'php') continue;
|
||||
$relativePath = str_replace($apiDir . DIRECTORY_SEPARATOR, '', $file->getPathname());
|
||||
$relativePath = str_replace('\\', '/', $relativePath);
|
||||
$apiOptions[] = 'api/' . $relativePath;
|
||||
}
|
||||
}
|
||||
sort($apiOptions);
|
||||
|
||||
return $this->render('api_tester', [
|
||||
'apiOptions' => $apiOptions,
|
||||
'defaultToken' => "15a437d128c918633e57dc7d18118a073b807644f21731ed5a7ebcf3eca9e242"
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\Documentation;
|
||||
|
||||
class DocumentationController extends Controller {
|
||||
public function index() {
|
||||
$query = $_GET['q'] ?? '';
|
||||
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||
$limit = 5;
|
||||
|
||||
$results = Documentation::search($query, $page, $limit);
|
||||
|
||||
return $this->render('documentation', [
|
||||
'docs' => $results['rows'],
|
||||
'total' => $results['total'],
|
||||
'pages' => $results['pages'],
|
||||
'currentPage' => $results['current_page'],
|
||||
'query' => $query
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\Domain;
|
||||
|
||||
class DomainController extends Controller {
|
||||
public function domains() {
|
||||
$statusMsg = "";
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST["add_domain"])) {
|
||||
$subdomain = trim($_POST["subdomain"]);
|
||||
if ($subdomain != "") {
|
||||
Domain::insert(['SUBDOMAIN' => $subdomain]);
|
||||
$statusMsg = "Domain wurde hinzugefügt.";
|
||||
}
|
||||
}
|
||||
if (isset($_POST["delete_domain"])) {
|
||||
Domain::delete("ID = ?", intval($_POST["id"]));
|
||||
$statusMsg = "Domain wurde gelöscht.";
|
||||
}
|
||||
if (isset($_POST["save_domain"])) {
|
||||
Domain::update(['SUBDOMAIN' => trim($_POST["subdomain"])], "ID = ?", intval($_POST["id"]));
|
||||
$statusMsg = "Domain wurde gespeichert.";
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('domains', [
|
||||
'domains' => Domain::all(),
|
||||
'statusMsg' => $statusMsg
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\Log;
|
||||
|
||||
class LogController extends Controller {
|
||||
protected function getDevIpsSql() {
|
||||
$devips = (new \getSpecificData())->getWhitelistedIPs();
|
||||
return "'" . implode("','", $devips) . "'";
|
||||
}
|
||||
|
||||
protected function handleLogs($websiteFilter, $serviceName) {
|
||||
$devips_sql = $this->getDevIpsSql();
|
||||
$filter = $_GET['filter'] ?? 'today';
|
||||
|
||||
$daten = Log::getFiltered($websiteFilter, $devips_sql, $filter);
|
||||
|
||||
return $this->render('logs', [
|
||||
'daten' => $daten,
|
||||
'filter' => $filter,
|
||||
'serviceName' => $serviceName
|
||||
]);
|
||||
}
|
||||
|
||||
public function public_request_logs() {
|
||||
return $this->handleLogs("(WEBSITE != 'backend.blackpi.ipv64.net' OR WEBSITE IS NULL)", 'public_request_logs');
|
||||
}
|
||||
|
||||
public function backend_request_logs() {
|
||||
return $this->handleLogs("(WEBSITE = 'backend.blackpi.ipv64.net')", 'backend_request_logs');
|
||||
}
|
||||
|
||||
public function personal_request_logs() {
|
||||
return $this->handleLogs("1=1", 'personal_request_logs');
|
||||
}
|
||||
|
||||
public function public_request_chart() {
|
||||
$totalData = Log::getChartData();
|
||||
$totalDataAssoc = [];
|
||||
$allLabels = [];
|
||||
foreach ($totalData as $row) {
|
||||
$totalDataAssoc[$row['tag']] = (int)$row['anzahl'];
|
||||
$allLabels[] = $row['tag'];
|
||||
}
|
||||
|
||||
$nodes = ['node1','node2','node3','node4'];
|
||||
$finalNodeData = [];
|
||||
foreach ($nodes as $node) {
|
||||
$nodeData = Log::getNodeChartData($node);
|
||||
$nodeAssoc = [];
|
||||
foreach ($nodeData as $row) {
|
||||
$nodeAssoc[$row['tag']] = (int)$row['anzahl'];
|
||||
if (!in_array($row['tag'], $allLabels)) $allLabels[] = $row['tag'];
|
||||
}
|
||||
|
||||
// Re-map to all labels
|
||||
$finalNodeData[$node] = $nodeAssoc;
|
||||
}
|
||||
sort($allLabels);
|
||||
|
||||
$finalTotalData = [];
|
||||
$mappedNodeData = [];
|
||||
foreach ($allLabels as $label) {
|
||||
$finalTotalData[] = $totalDataAssoc[$label] ?? 0;
|
||||
foreach ($nodes as $node) {
|
||||
$mappedNodeData[$node][] = $finalNodeData[$node][$label] ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('public_chart', [
|
||||
'allLabels' => $allLabels,
|
||||
'finalTotalData' => $finalTotalData,
|
||||
'finalNodeData' => $mappedNodeData
|
||||
]);
|
||||
}
|
||||
|
||||
public function backend_request_chart() {
|
||||
$data = Log::getChartData("WEBSITE = 'backend.blackpi.ipv64.net'");
|
||||
$labels = [];
|
||||
$counts = [];
|
||||
foreach ($data as $row) {
|
||||
$labels[] = $row['tag'];
|
||||
$counts[] = (int)$row['anzahl'];
|
||||
}
|
||||
|
||||
return $this->render('backend_chart', [
|
||||
'labels' => $labels,
|
||||
'data' => $counts
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\CompanyReport;
|
||||
use MVC\Model\SchoolReport;
|
||||
use MVC\Model\Overtime;
|
||||
|
||||
class ReportController extends Controller {
|
||||
public function berichte() {
|
||||
$mode = $_GET['mode'] ?? 'fk';
|
||||
|
||||
if ($mode === 'normal') {
|
||||
$data = CompanyReport::all();
|
||||
} else {
|
||||
$data = SchoolReport::allWithFK();
|
||||
}
|
||||
|
||||
$data = array_reverse($data);
|
||||
$displayData = (is_array($data) && isset($data['rows'])) ? $data['rows'] : $data;
|
||||
$count = is_array($displayData) ? count($displayData) : 0;
|
||||
|
||||
return $this->render('berichte', [
|
||||
'mode' => $mode,
|
||||
'displayData' => $displayData,
|
||||
'count' => $count,
|
||||
'selectedService' => $_GET['service'] ?? 'berichte'
|
||||
]);
|
||||
}
|
||||
|
||||
public function berichte_arbeit_gen() {
|
||||
if (empty($_SESSION['user_logged_in'])) return "Access Denied";
|
||||
|
||||
$wochenArray = CompanyReport::getWochen();
|
||||
|
||||
$startDate = $_POST['start_date'] ?? date('Y-m-d', strtotime('-5 days'));
|
||||
$endDate = $_POST['end_date'] ?? date('Y-m-d');
|
||||
|
||||
$berichteArray = CompanyReport::getByDateRange($startDate, $endDate);
|
||||
|
||||
$pdfFile = null;
|
||||
if (isset($_POST['generate_pdf']) && count($berichteArray) > 0) {
|
||||
$pdfFile = $this->generatePDF($berichteArray);
|
||||
}
|
||||
|
||||
return $this->render('berichte_arbeit_gen', [
|
||||
'startDate' => $startDate,
|
||||
'endDate' => $endDate,
|
||||
'wochenArray' => $wochenArray,
|
||||
'pdfFile' => $pdfFile,
|
||||
'berichteArray' => $berichteArray
|
||||
]);
|
||||
}
|
||||
|
||||
protected function generatePDF($berichteArray) {
|
||||
$Einzelstunden = "2";
|
||||
$Gesamtstunden = "8";
|
||||
$data = [
|
||||
'Text1' => '', // Nr
|
||||
'Text2' => $berichteArray[0]['DATUM'] ?? '',
|
||||
'Text3' => end($berichteArray)['DATUM'] ?? '',
|
||||
'Text4' => 'Eckhardt',
|
||||
'Text5' => 'Bruno Renato',
|
||||
'Text6' => '1',
|
||||
];
|
||||
|
||||
$fieldIndex = 7;
|
||||
foreach ($berichteArray as $bericht) {
|
||||
for ($i = 1; $i <= 4; $i++) {
|
||||
$data["Text$fieldIndex"] = $bericht["PROZESS_$i"] ?? '';
|
||||
$fieldIndex++;
|
||||
$data["Text$fieldIndex"] = $Einzelstunden;
|
||||
$fieldIndex++;
|
||||
}
|
||||
$data["Text$fieldIndex"] = $Gesamtstunden;
|
||||
$fieldIndex++;
|
||||
}
|
||||
|
||||
$fdfFields = array_map(function($field, $value){
|
||||
$value = str_replace(['(', ')'], ['\(', '\)'], $value);
|
||||
return "<< /T ($field) /V ($value) >>";
|
||||
}, array_keys($data), $data);
|
||||
|
||||
$fdf = "%FDF-1.2\n1 0 obj\n<<\n/FDF << /Fields [\n" . implode("\n", $fdfFields) . "\n] >> >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF";
|
||||
|
||||
$fdfFile = tempnam(sys_get_temp_dir(), 'fdf_') . '.fdf';
|
||||
file_put_contents($fdfFile, $fdf);
|
||||
|
||||
$specificData = new \getSpecificData();
|
||||
$inputPdf = $specificData->getDocumentRoot() . '/templates/ausbildungsnachweis.pdf';
|
||||
$outputPdf = $specificData->getDocumentRoot() . '/static/ausbildungsnachweis_' . $berichteArray[0]['DATUM'] . '-' . end($berichteArray)['DATUM'] . '.pdf';
|
||||
|
||||
exec("pdftk $inputPdf fill_form $fdfFile output $outputPdf flatten");
|
||||
unlink($fdfFile);
|
||||
|
||||
return $outputPdf;
|
||||
}
|
||||
|
||||
public function zeiterfassung() {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_overtime'])) {
|
||||
Overtime::insert([
|
||||
'DATUM' => $_POST['ot_date'],
|
||||
'MINUTEN' => (int)$_POST['ot_minutes'],
|
||||
'GRUND' => $_POST['ot_reason']
|
||||
]);
|
||||
\redirectToWebsite((new \getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
$minuten_berichte = CompanyReport::getTotalOvertime();
|
||||
$minuten_abbau = Overtime::getSumByReason('Abbau');
|
||||
$minuten_aufbau = Overtime::getSumByReason('Aufbau');
|
||||
|
||||
$gesamt_minuten = $minuten_berichte - $minuten_abbau + $minuten_aufbau;
|
||||
|
||||
return $this->render('zeiterfassung', [
|
||||
'gesamt_minuten' => $gesamt_minuten,
|
||||
'username' => $_SESSION['username']
|
||||
]);
|
||||
}
|
||||
|
||||
public function print_static() {
|
||||
$dir = (new \getSpecificData())->getDocumentRoot() . "/static";
|
||||
$files = is_dir($dir) ? array_diff(scandir($dir), ['.', '..']) : [];
|
||||
|
||||
return $this->render('print_static', ['files' => $files]);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
|
||||
class ServiceController extends Controller {
|
||||
public function home_info() {
|
||||
$username = $_SESSION['username'] ?? 'Gast';
|
||||
$is_superuser = $_SESSION['is_superuser'] ?? false;
|
||||
$is_admin = $_SESSION['is_admin'] ?? false;
|
||||
|
||||
$db_res = \DatabaseShow(
|
||||
"Benutzer",
|
||||
"NACHNAME, EMAIL, ROLLE, HOME_IP",
|
||||
"NAME = ?",
|
||||
$username
|
||||
);
|
||||
|
||||
return $this->render('home_info', [
|
||||
'username' => $username,
|
||||
'is_superuser' => $is_superuser,
|
||||
'is_admin' => $is_admin,
|
||||
'db_res' => $db_res
|
||||
]);
|
||||
}
|
||||
|
||||
public function links() {
|
||||
global $linkButtons, $linkGroups, $isAdmin;
|
||||
|
||||
return $this->render('links', [
|
||||
'linkButtons' => $linkButtons,
|
||||
'linkGroups' => $linkGroups,
|
||||
'isAdmin' => $isAdmin
|
||||
]);
|
||||
}
|
||||
|
||||
public function logout() {
|
||||
session_destroy();
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Placeholder for other services
|
||||
public function __call($name, $arguments) {
|
||||
return "Service $name logic not implemented yet in Controller.";
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\Domain;
|
||||
|
||||
class StatsController extends Controller {
|
||||
public function weather_chart() {
|
||||
// Since Weather is not yet a dedicated model, we use the Domain query helper or raw query
|
||||
$res = Domain::query("SELECT ZEIT, AKTUELL FROM Wetter");
|
||||
$labels = [];
|
||||
$data = [];
|
||||
while($row = $res->fetch_assoc()) {
|
||||
$labels[] = $row['ZEIT'];
|
||||
$data[] = (float)$row['AKTUELL'];
|
||||
}
|
||||
|
||||
return $this->render('weather_chart', [
|
||||
'labels' => $labels,
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Core;
|
||||
|
||||
class Controller {
|
||||
protected $view;
|
||||
|
||||
public function __construct() {
|
||||
$this->view = new View(__DIR__ . '/../View');
|
||||
}
|
||||
|
||||
protected function render($template, $data = []) {
|
||||
foreach ($data as $key => $value) {
|
||||
$this->view->assign($key, $value);
|
||||
}
|
||||
return $this->view->render($template);
|
||||
}
|
||||
|
||||
protected function json($data, $status = 200) {
|
||||
header('Content-Type: application/json');
|
||||
http_response_code($status);
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Core;
|
||||
|
||||
abstract class Model {
|
||||
protected static $table;
|
||||
protected static $conn;
|
||||
|
||||
protected static function getConnection() {
|
||||
if (!self::$conn) {
|
||||
self::$conn = \db_connect();
|
||||
}
|
||||
return self::$conn;
|
||||
}
|
||||
|
||||
public static function all() {
|
||||
return \DatabaseShow(static::$table, "*");
|
||||
}
|
||||
|
||||
public static function find($id, $column = 'ID') {
|
||||
$result = \DatabaseShow(static::$table, "*", "$column = ?", $id);
|
||||
return $result[0] ?? null;
|
||||
}
|
||||
|
||||
public static function where($where, ...$params) {
|
||||
return \DatabaseShow(static::$table, "*", $where, ...$params);
|
||||
}
|
||||
|
||||
public static function insert($data) {
|
||||
$columns = implode(", ", array_keys($data));
|
||||
$values = array_values($data);
|
||||
return \DatabaseInsert(static::$table, $columns, ...$values);
|
||||
}
|
||||
|
||||
public static function update($data, $where, ...$whereParams) {
|
||||
$sets = [];
|
||||
$values = [];
|
||||
foreach ($data as $column => $value) {
|
||||
$sets[] = "$column = ?";
|
||||
$values[] = $value;
|
||||
}
|
||||
$setString = implode(", ", $sets);
|
||||
return \DatabaseUpdate(static::$table, $setString, $where, ...array_merge($values, $whereParams));
|
||||
}
|
||||
|
||||
public static function delete($where, ...$params) {
|
||||
return \DatabaseDelete(static::$table, $where, ...$params);
|
||||
}
|
||||
|
||||
public static function query($sql, ...$params) {
|
||||
$conn = self::getConnection();
|
||||
if (empty($params)) {
|
||||
return $conn->query($sql);
|
||||
}
|
||||
$stmt = $conn->prepare($sql);
|
||||
$types = str_repeat('s', count($params));
|
||||
$stmt->bind_param($types, ...$params);
|
||||
$stmt->execute();
|
||||
return $stmt->get_result();
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Core;
|
||||
|
||||
class Router {
|
||||
protected $routes = [];
|
||||
protected $middlewares = [];
|
||||
|
||||
public function add($service, $controller, $method, $middlewares = []) {
|
||||
$this->routes[$service] = [
|
||||
'controller' => $controller,
|
||||
'method' => $method,
|
||||
'middlewares' => $middlewares
|
||||
];
|
||||
}
|
||||
|
||||
public function dispatch($service) {
|
||||
if (!isset($this->routes[$service])) {
|
||||
throw new \Exception("Service '$service' not found.");
|
||||
}
|
||||
|
||||
$route = $this->routes[$service];
|
||||
|
||||
// Run Middlewares
|
||||
foreach ($route['middlewares'] as $middleware) {
|
||||
$this->runMiddleware($middleware);
|
||||
}
|
||||
|
||||
$controllerClass = $route['controller'];
|
||||
$method = $route['method'];
|
||||
|
||||
if (!class_exists($controllerClass)) {
|
||||
throw new \Exception("Controller class '$controllerClass' not found.");
|
||||
}
|
||||
|
||||
$controller = new $controllerClass();
|
||||
|
||||
if (!method_exists($controller, $method)) {
|
||||
throw new \Exception("Method '$method' not found in controller '$controllerClass'.");
|
||||
}
|
||||
|
||||
return $controller->$method();
|
||||
}
|
||||
|
||||
protected function runMiddleware($middleware) {
|
||||
if ($middleware === 'admin') {
|
||||
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
|
||||
die("Access Denied: Admin rights required.");
|
||||
}
|
||||
}
|
||||
|
||||
if ($middleware === 'auth') {
|
||||
if (empty($_SESSION['user_logged_in'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Core;
|
||||
|
||||
class View {
|
||||
protected $data = [];
|
||||
protected $viewPath;
|
||||
|
||||
public function __construct($viewPath) {
|
||||
$this->viewPath = $viewPath;
|
||||
}
|
||||
|
||||
public function assign($key, $value) {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
public function render($template) {
|
||||
extract($this->data);
|
||||
$file = $this->viewPath . '/' . $template . '.php';
|
||||
if (file_exists($file)) {
|
||||
ob_start();
|
||||
include $file;
|
||||
return ob_get_clean();
|
||||
}
|
||||
return "View $template not found at $file";
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Layout;
|
||||
|
||||
class Layout {
|
||||
protected $blocks = [];
|
||||
protected $template;
|
||||
|
||||
public function __construct($template = 'main') {
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
public function setBlock($name, $content) {
|
||||
$this->blocks[$name] = $content;
|
||||
}
|
||||
|
||||
public function getBlock($name) {
|
||||
return $this->blocks[$name] ?? '';
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$layoutFile = __DIR__ . '/Templates/' . $this->template . '.php';
|
||||
if (file_exists($layoutFile)) {
|
||||
ob_start();
|
||||
include $layoutFile;
|
||||
return ob_get_clean();
|
||||
}
|
||||
return "Layout template {$this->template} not found.";
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $this->getBlock('title') ?></title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<?= $this->getBlock('head') ?>
|
||||
</head>
|
||||
<body>
|
||||
<header class="main-header">
|
||||
<?= $this->getBlock('header') ?>
|
||||
</header>
|
||||
<div class="main">
|
||||
<div class="sidebar">
|
||||
<?= $this->getBlock('sidebar') ?>
|
||||
</div>
|
||||
<div class="content">
|
||||
<?= $this->getBlock('content') ?>
|
||||
</div>
|
||||
</div>
|
||||
<script src="main.js"></script>
|
||||
<?= $this->getBlock('scripts') ?>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,121 +0,0 @@
|
||||
# 🏗️ Backend MVC - Umfassende Dokumentation
|
||||
|
||||
Diese Dokumentation beschreibt die Architektur, die Funktionsweise und die Besonderheiten des neuen Backend-Systems.
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ 1. Architektur-Übersicht
|
||||
|
||||
Das System folgt dem **MVC (Model-View-Controller)** Pattern. Das Ziel ist eine strikte Trennung von Datenverarbeitung, Geschäftslogik und Benutzeroberfläche.
|
||||
|
||||
### 📂 Verzeichnisstruktur & Verantwortlichkeiten
|
||||
- **`WEBROOT/index.php`**: Der zentrale Einstiegspunkt. Hier wird das Routing initialisiert, die Layout-Blöcke definiert und das Error-Handling gesteuert.
|
||||
- **`src/MVC/Core/`**: Das Herzstück. Enthält die Basisklassen `Controller`, `Model`, `View`, `Router` und `Layout`. Diese Klassen sollten selten angefasst werden.
|
||||
- **`src/MVC/Model/`**: Daten-Logik. Jede Klasse repräsentiert eine Datenbanktabelle oder einen logischen Datenbereich.
|
||||
- **`src/MVC/Controller/`**: Geschäftslogik. Controller nehmen Anfragen entgegen, nutzen Models zur Datenbeschaffung und geben Daten an Views weiter.
|
||||
- **`src/MVC/View/`**: Präsentationsschicht. Reine HTML/PHP-Dateien für die Anzeige.
|
||||
- **`src/MVC/Layout/`**: Definition des Rahmendesigns und Verwaltung der Inhaltsblöcke.
|
||||
|
||||
---
|
||||
|
||||
## 🚦 2. Routing & Middleware (Der Verkehrspolizist)
|
||||
|
||||
Das Routing wird in der `index.php` über den `Router` konfiguriert.
|
||||
|
||||
### Routen registrieren
|
||||
```php
|
||||
$router->add($service_id, $controller_class, $method_name, $middlewares);
|
||||
```
|
||||
- **`$service_id`**: Der Wert des URL-Parameters `?service=...`.
|
||||
- **`$middlewares`**: Ein Array von Sicherheitsstufen (z.B. `['auth', 'admin']`).
|
||||
|
||||
### Verfügbare Middlewares
|
||||
- `auth`: Stellt sicher, dass die Session `user_logged_in` aktiv ist. Leitet sonst zum Login um.
|
||||
- `admin`: Stellt sicher, dass die Session `is_admin` wahr ist. Gibt sonst "Access Denied" aus.
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ 3. Der Model-Layer (Datenabstraktion)
|
||||
|
||||
Alle Models erben von `MVC\Core\Model`. Dies gibt ihnen Zugriff auf mächtige Standardmethoden:
|
||||
|
||||
### CRUD-Operationen
|
||||
- `Model::all()`: Gibt alle Datensätze der Tabelle zurück.
|
||||
- `Model::find($id)`: Sucht einen Datensatz primär über die ID.
|
||||
- `Model::where($sql, ...$params)`: Filtert Datensätze mit Prepared Statements.
|
||||
- `Model::insert($data)`: Erwartet ein assoziatives Array (z.B. `['NAME' => 'Max']`).
|
||||
- `Model::update($data, $where, ...$whereParams)`: Aktualisiert Felder basierend auf einer Bedingung.
|
||||
- `Model::delete($where, ...$params)`: Löscht Datensätze.
|
||||
- `Model::query($sql, ...$params)`: Ermöglicht komplexe, manuelle SQL-Abfragen.
|
||||
|
||||
### 💡 Besonderheit: Statische Bindung
|
||||
In den Models wird die Tabelle über `protected static $table` definiert. Die Basisklasse nutzt `static::`, um immer auf die Tabelle des Kind-Models zuzugreifen.
|
||||
|
||||
---
|
||||
|
||||
## 🎮 4. Controller & AJAX (Die Logik)
|
||||
|
||||
Controller fungieren als Vermittler. Sie verarbeiten sowohl klassische Seitenaufrufe als auch AJAX-Requests.
|
||||
|
||||
### Seiten rendern
|
||||
```php
|
||||
return $this->render('view_name', ['key' => 'value']);
|
||||
```
|
||||
|
||||
### JSON-Antworten (für AJAX)
|
||||
Für moderne Oberflächen, die Daten nachladen oder umschalten ohne Refresh:
|
||||
```php
|
||||
return $this->json(['success' => true, 'data' => $result]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧱 5. Das modulare Block-Layout
|
||||
|
||||
Das Layout ist in "Blöcke" unterteilt, die in der `index.php` befüllt werden.
|
||||
|
||||
### Blöcke setzen
|
||||
```php
|
||||
$layout->setBlock('header', $html_content);
|
||||
$layout->setBlock('sidebar', $sidebar_html);
|
||||
```
|
||||
|
||||
### Blöcke in der View nutzen (Layout-Template)
|
||||
In `Layout/Templates/main.php` werden die Blöcke mit `$this->getBlock('name')` ausgegeben.
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ 6. Error-Handling & Sicherheit
|
||||
|
||||
### Zentrales Catch-System
|
||||
Die gesamte Applikation läuft in einem `try-catch` Block.
|
||||
- Tritt ein Fehler (Exception) auf, wird dieser im System-Log gespeichert.
|
||||
- **Admins** sehen den "Stacktrace" (Pfad des Fehlers) zur schnellen Behebung.
|
||||
- **Benutzer** sehen eine generische, freundliche Fehlermeldung.
|
||||
|
||||
### SQL-Injection Schutz
|
||||
Durch die konsequente Nutzung von `Model::where`, `Model::insert` etc., werden automatisch **Prepared Statements** genutzt. SQL-Injections sind dadurch nahezu ausgeschlossen.
|
||||
|
||||
### XSS-Schutz
|
||||
Variablen in der View sollten **immer** mit `htmlspecialchars()` ausgegeben werden:
|
||||
```html
|
||||
<p><?= htmlspecialchars($user_name) ?></p>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 7. Besonderheiten & Tipps
|
||||
|
||||
- **Namespace-Beachtung**: Da wir Namespaces nutzen (`MVC\Controller`), müssen globale PHP-Funktionen oder Klassen aus dem `src/utils` Ordner mit einem Backslash aufgerufen werden (z.B. `\db_connect()` oder `\getSpecificData()`).
|
||||
- **Globaler State**: Variablen aus der `config.php` (wie `$Berechtigungen`) müssen in Controllern oft via `global $variable;` bekannt gemacht werden, bevor sie an Models übergeben werden.
|
||||
- **Post-Redirect-Get**: Nach dem Speichern von Daten (`POST`) im Controller immer ein `header("Location: ...")` ausführen, um doppelte Einsendungen beim Neuladen zu verhindern.
|
||||
- **Statische Files**: Bilder, CSS und JS gehören in den Ordner `WEBROOT/static/`.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Modul-Generator
|
||||
Nutze das Script im Hauptverzeichnis für schnelle Ergebnisse:
|
||||
```bash
|
||||
php create_module.php mein_name
|
||||
```
|
||||
Dieses erstellt automatisch Controller und View nach den oben genannten Standards.
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class CompanyReport extends Model {
|
||||
protected static $table = "Firmenberichte";
|
||||
|
||||
public static function getByDateRange($start, $end) {
|
||||
$sql = "SELECT `DATUM`, `PROZESS_1`, `PROZESS_2`, `PROZESS_3`, `PROZESS_4` FROM Firmenberichte WHERE `DATUM` BETWEEN ? AND ?";
|
||||
return self::query($sql, $start, $end)->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public static function getWochen() {
|
||||
self::query("SET @row := 0");
|
||||
$sql = "SELECT MIN(DATUM) AS start_date, MAX(DATUM) AS end_date FROM (SELECT DATUM, DATE_SUB(DATUM, INTERVAL (@row := @row + 1) DAY) AS grp FROM Firmenberichte ORDER BY DATUM) AS t GROUP BY grp ORDER BY start_date";
|
||||
return self::query($sql)->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public static function getTotalOvertime() {
|
||||
$res = self::query("SELECT SUM(OVERTIME) AS summe FROM Firmenberichte");
|
||||
return intval($res->fetch_object()->summe ?? 0);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Documentation extends Model {
|
||||
protected static $table = "Dokumentation";
|
||||
|
||||
public static function search($query, $page = 1, $limit = 5) {
|
||||
$offset = ($page - 1) * $limit;
|
||||
$search = "%$query%";
|
||||
|
||||
$countRes = \DatabaseShow(self::$table, "COUNT(*) as total", "TITEL LIKE ? OR INHALT LIKE ? OR KATEGORIE LIKE ?", $search, $search, $search);
|
||||
$total = $countRes[0]['total'] ?? 0;
|
||||
|
||||
$sql = "SELECT * FROM Dokumentation WHERE TITEL LIKE ? OR INHALT LIKE ? OR KATEGORIE LIKE ? ORDER BY ERSTELLT_AM DESC LIMIT ? OFFSET ?";
|
||||
$result = self::query($sql, $search, $search, $search, $limit, $offset);
|
||||
|
||||
$rows = $result->fetch_all(MYSQLI_ASSOC);
|
||||
|
||||
return [
|
||||
'rows' => $rows,
|
||||
'total' => $total,
|
||||
'pages' => ceil($total / $limit),
|
||||
'current_page' => $page
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Domain extends Model {
|
||||
protected static $table = "Domains";
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Log extends Model {
|
||||
protected static $table = "logs";
|
||||
|
||||
public static function getFiltered($websiteFilter, $devIpsSql, $filter) {
|
||||
switch ($filter) {
|
||||
case 'today':
|
||||
$sql = "SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE` FROM logs WHERE DATE(FIRST_REQUEST) = CURDATE() AND $websiteFilter AND CLIENT_IP NOT IN ($devIpsSql) ORDER BY FIRST_REQUEST DESC";
|
||||
break;
|
||||
case 'counterperip':
|
||||
$sql = "SELECT CLIENT_IP, LAND, USER_AGENT, SUM(counter) AS gesamt_counter FROM logs WHERE $websiteFilter AND CLIENT_IP NOT IN ($devIpsSql) GROUP BY CLIENT_IP ORDER BY gesamt_counter DESC";
|
||||
break;
|
||||
case 'node1':
|
||||
case 'node2':
|
||||
case 'node3':
|
||||
case 'node4':
|
||||
$sql = "SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE` FROM logs WHERE `SERVER_IP` = ? AND $websiteFilter ORDER BY `FIRST_REQUEST` DESC";
|
||||
$res = self::query($sql, $filter);
|
||||
return $res->fetch_all(MYSQLI_ASSOC);
|
||||
default:
|
||||
$sql = "SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE` FROM logs WHERE $websiteFilter AND CLIENT_IP NOT IN ($devIpsSql) ORDER BY FIRST_REQUEST DESC";
|
||||
}
|
||||
|
||||
$res = self::query($sql);
|
||||
return $res->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public static function getChartData($websiteFilter = null) {
|
||||
$where = $websiteFilter ? "WHERE $websiteFilter" : "";
|
||||
$sql = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs $where GROUP BY DATE(FIRST_REQUEST) ORDER BY DATE(FIRST_REQUEST)";
|
||||
return self::query($sql)->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public static function getNodeChartData($node) {
|
||||
$sql = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs WHERE SERVER_IP = ? GROUP BY DATE(FIRST_REQUEST)";
|
||||
return self::query($sql, $node)->fetch_all(MYSQLI_ASSOC);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Overtime extends Model {
|
||||
protected static $table = "Ueberstunden";
|
||||
|
||||
public static function getSumByReason($reason) {
|
||||
$res = self::query("SELECT SUM(MINUTEN) AS summe FROM Ueberstunden WHERE GRUND = ?", $reason);
|
||||
return intval($res->fetch_object()->summe ?? 0);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class SchoolReport extends Model {
|
||||
protected static $table = "Berufsschulberichte";
|
||||
|
||||
public static function allWithFK() {
|
||||
global $Berufsschulberichte;
|
||||
return \DatabaseShowWithFK(self::$table, $Berufsschulberichte);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class User extends Model {
|
||||
protected static $table = "Benutzer";
|
||||
|
||||
public static function getByName($name) {
|
||||
return self::where("NAME = ?", $name);
|
||||
}
|
||||
|
||||
public static function isAdmin($userId) {
|
||||
$user = self::find($userId);
|
||||
return ($user['ADMIN'] ?? 0) == 1;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 123 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user