new
This commit is contained in:
1 parent
d24a674643
commit
3a86acc057
22 files changed
+916
-334
No files matched your search
+8
-2
@@ -17,7 +17,7 @@ $serviceGroups = array (
|
||||
array (
|
||||
0 => 'home_info',
|
||||
1 => 'links',
|
||||
2 => 'tester',
|
||||
3 => 'documentation',
|
||||
),
|
||||
),
|
||||
'Ausbildung' =>
|
||||
@@ -192,6 +192,12 @@ $services = array (
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
'documentation' =>
|
||||
array (
|
||||
'name' => 'Dokumentation',
|
||||
'active' => true,
|
||||
'adminOnly' => false,
|
||||
),
|
||||
);
|
||||
|
||||
$linkGroups = array (
|
||||
@@ -382,4 +388,4 @@ $linkButtons = array (
|
||||
'adminOnly' => false,
|
||||
'group' => 'tools',
|
||||
),
|
||||
);
|
||||
);
|
||||
+75
-61
@@ -88,37 +88,68 @@ if ((new getSpecificData())->getWebsite() != "backend.blackpi.ipv64.net") {
|
||||
}
|
||||
|
||||
// MVC Logic
|
||||
$layout = new \MVC\Layout\Layout('main');
|
||||
$layout->setBlock('title', $title);
|
||||
try {
|
||||
$router = new \MVC\Core\Router();
|
||||
|
||||
// Header Block
|
||||
ob_start();
|
||||
?>
|
||||
// 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']);
|
||||
|
||||
$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']);
|
||||
|
||||
$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']);
|
||||
|
||||
$router->add('api_tester', \MVC\Controller\TestController::class, 'api_tester', ['auth', 'admin']);
|
||||
$router->add('weather_chart', \MVC\Controller\TestController::class, 'weather_chart', ['auth', 'admin']);
|
||||
$router->add('domains', \MVC\Controller\TestController::class, 'domains', ['auth']);
|
||||
$router->add('documentation', \MVC\Controller\DocumentationController::class, 'index', ['auth']);
|
||||
|
||||
$layout = new \MVC\Layout\Layout('main');
|
||||
$layout->setBlock('title', $title);
|
||||
|
||||
// Header Block
|
||||
ob_start();
|
||||
?>
|
||||
Backend
|
||||
<button id="darkModeToggle">Dark Mode</button>
|
||||
<button id="logoutButton">Logout</button>
|
||||
<?php
|
||||
$layout->setBlock('header', ob_get_clean());
|
||||
$layout->setBlock('header', ob_get_clean());
|
||||
|
||||
// Sidebar Block
|
||||
ob_start();
|
||||
?>
|
||||
// Sidebar Block
|
||||
ob_start();
|
||||
?>
|
||||
<?php foreach ($serviceGroups as $groupKey => $group): ?>
|
||||
<?php
|
||||
$visibleServices = [];
|
||||
$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;
|
||||
}
|
||||
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;
|
||||
if (empty($visibleServices)) continue;
|
||||
|
||||
$isOpen = !empty($group['defaultOpen']) || in_array($selectedService, $visibleServices, true);
|
||||
?>
|
||||
$isOpen = !empty($group['defaultOpen']) || in_array($selectedService, $visibleServices, true);
|
||||
?>
|
||||
<div class="service-group <?= $isOpen ? 'open' : '' ?>" data-group="<?= $groupKey ?>">
|
||||
|
||||
<div class="group-title">
|
||||
@@ -138,48 +169,31 @@ ob_start();
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php
|
||||
$layout->setBlock('sidebar', ob_get_clean());
|
||||
$layout->setBlock('sidebar', ob_get_clean());
|
||||
|
||||
// Content Block
|
||||
if ($selectedService) {
|
||||
$controllers = [
|
||||
'home_info' => \MVC\Controller\ServiceController::class,
|
||||
'links' => \MVC\Controller\ServiceController::class,
|
||||
'logout' => \MVC\Controller\ServiceController::class,
|
||||
'public_request_logs' => \MVC\Controller\LogController::class,
|
||||
'backend_request_logs' => \MVC\Controller\LogController::class,
|
||||
'personal_request_logs' => \MVC\Controller\LogController::class,
|
||||
'public_request_chart' => \MVC\Controller\LogController::class,
|
||||
'backend_request_chart' => \MVC\Controller\LogController::class,
|
||||
'user_control' => \MVC\Controller\AdminController::class,
|
||||
'services_control' => \MVC\Controller\AdminController::class,
|
||||
'variablen_control' => \MVC\Controller\AdminController::class,
|
||||
'data_info' => \MVC\Controller\AdminController::class,
|
||||
'tabellen' => \MVC\Controller\AdminController::class,
|
||||
'berichte' => \MVC\Controller\ReportController::class,
|
||||
'berichte_arbeit_gen' => \MVC\Controller\ReportController::class,
|
||||
'zeiterfassung' => \MVC\Controller\ReportController::class,
|
||||
'print_static' => \MVC\Controller\ReportController::class,
|
||||
'api_tester' => \MVC\Controller\TestController::class,
|
||||
'weather_chart' => \MVC\Controller\TestController::class,
|
||||
'domains' => \MVC\Controller\TestController::class,
|
||||
];
|
||||
|
||||
$controllerClass = $controllers[$selectedService] ?? null;
|
||||
|
||||
if ($controllerClass) {
|
||||
$controller = new $controllerClass();
|
||||
$content = $controller->$selectedService();
|
||||
// Content Block
|
||||
if ($selectedService) {
|
||||
$content = $router->dispatch($selectedService);
|
||||
$layout->setBlock('content', $content);
|
||||
logging();
|
||||
} else {
|
||||
// Fallback to old services if not yet refactored
|
||||
ob_start();
|
||||
include "services/$selectedService.php";
|
||||
$content = ob_get_clean();
|
||||
$layout->setBlock('content', "<p>Kein aktiver Service verfügbar.</p>");
|
||||
}
|
||||
$layout->setBlock('content', $content);
|
||||
logging();
|
||||
} else {
|
||||
$layout->setBlock('content', "<p>Kein aktiver Service verfügbar.</p>");
|
||||
}
|
||||
|
||||
echo $layout->render();
|
||||
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>";
|
||||
}
|
||||
}
|
||||
@@ -3,37 +3,31 @@
|
||||
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();
|
||||
|
||||
// Form Processing
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['add_user']) && $isAdmin) {
|
||||
$userCtrl = new \UserController();
|
||||
$userCtrl->setUser(
|
||||
$_POST['name'],
|
||||
$_POST['nachname'],
|
||||
$_POST['email'],
|
||||
$_POST['passwort'],
|
||||
$_POST['rolle'],
|
||||
$_POST['admin'] ?? 0,
|
||||
$_POST['home_ip']
|
||||
);
|
||||
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) {
|
||||
$userCtrl = new \UserController();
|
||||
$userCtrl->DeleteUser(
|
||||
$_POST['delete_name'],
|
||||
$_POST['delete_nachname'],
|
||||
$_POST['delete_email']
|
||||
);
|
||||
User::delete("NAME = ? AND NACHNAME = ? AND EMAIL = ?", $_POST['delete_name'], $_POST['delete_nachname'], $_POST['delete_email']);
|
||||
header("Location: " . $specificData->getReferer());
|
||||
exit;
|
||||
}
|
||||
@@ -53,17 +47,7 @@ class AdminController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
// Data Fetching
|
||||
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()
|
||||
);
|
||||
}
|
||||
$users = $isAdmin ? User::all() : User::getByName($specificData->getUsername());
|
||||
|
||||
return $this->render('user_control', [
|
||||
'users' => $users,
|
||||
@@ -73,6 +57,19 @@ class AdminController extends Controller {
|
||||
]);
|
||||
}
|
||||
|
||||
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";
|
||||
@@ -188,24 +185,6 @@ class AdminController extends Controller {
|
||||
]);
|
||||
}
|
||||
|
||||
public function variablen_control() {
|
||||
global $isAdmin;
|
||||
if (!$isAdmin) return "Access Denied";
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && 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");
|
||||
return $this->render('variablen_control', ['variablen' => $variablen]);
|
||||
}
|
||||
|
||||
public function data_info() {
|
||||
global $isAdmin;
|
||||
if (!$isAdmin) return "Access Denied";
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\Log;
|
||||
|
||||
class LogController extends Controller {
|
||||
protected function getDevIpsSql() {
|
||||
@@ -11,34 +12,10 @@ class LogController extends Controller {
|
||||
}
|
||||
|
||||
protected function handleLogs($websiteFilter, $serviceName) {
|
||||
$conn = \db_connect();
|
||||
$devips_sql = $this->getDevIpsSql();
|
||||
$filter = $_GET['filter'] ?? 'today';
|
||||
|
||||
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 ($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 $websiteFilter AND CLIENT_IP NOT IN ($devips_sql) GROUP BY CLIENT_IP ORDER BY gesamt_counter DESC";
|
||||
break;
|
||||
case 'node1':
|
||||
case 'node2':
|
||||
case 'node3':
|
||||
case 'node4':
|
||||
$node = $filter;
|
||||
$sql = "SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE` FROM logs WHERE `SERVER_IP` = '$node' AND $websiteFilter ORDER BY `FIRST_REQUEST` DESC";
|
||||
break;
|
||||
default:
|
||||
$sql = "SELECT `CLIENT_IP`, `LAND`, `REQUEST_URL`, `COUNTER`, `FIRST_REQUEST`, `WEBSITE` FROM logs WHERE $websiteFilter AND CLIENT_IP NOT IN ($devips_sql) ORDER BY FIRST_REQUEST DESC";
|
||||
}
|
||||
|
||||
$daten = [];
|
||||
if ($erg = $conn->query($sql)) {
|
||||
while ($row = $erg->fetch_assoc()) {
|
||||
$daten[] = $row;
|
||||
}
|
||||
}
|
||||
$daten = Log::getFiltered($websiteFilter, $devips_sql, $filter);
|
||||
|
||||
return $this->render('logs', [
|
||||
'daten' => $daten,
|
||||
@@ -60,64 +37,57 @@ class LogController extends Controller {
|
||||
}
|
||||
|
||||
public function public_request_chart() {
|
||||
$conn = \db_connect();
|
||||
$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);
|
||||
|
||||
$totalData = Log::getChartData();
|
||||
$totalDataAssoc = [];
|
||||
$allLabels = [];
|
||||
while($row = $result_total->fetch_assoc()) {
|
||||
$date = $row['tag'];
|
||||
$totalDataAssoc[$date] = (int)$row['anzahl'];
|
||||
$allLabels[] = $date;
|
||||
foreach ($totalData as $row) {
|
||||
$totalDataAssoc[$row['tag']] = (int)$row['anzahl'];
|
||||
$allLabels[] = $row['tag'];
|
||||
}
|
||||
|
||||
$nodes = ['node1','node2','node3','node4'];
|
||||
$nodeDataAssoc = [];
|
||||
$finalNodeData = [];
|
||||
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 = [];
|
||||
while($row = $result->fetch_assoc()) {
|
||||
$date = $row['tag'];
|
||||
$data[$date] = (int)$row['anzahl'];
|
||||
if (!in_array($date, $allLabels)) $allLabels[] = $date;
|
||||
$nodeData = Log::getNodeChartData($node);
|
||||
$nodeAssoc = [];
|
||||
foreach ($nodeData as $row) {
|
||||
$nodeAssoc[$row['tag']] = (int)$row['anzahl'];
|
||||
if (!in_array($row['tag'], $allLabels)) $allLabels[] = $row['tag'];
|
||||
}
|
||||
$nodeDataAssoc[$node] = $data;
|
||||
|
||||
// Re-map to all labels
|
||||
$finalNodeData[$node] = $nodeAssoc;
|
||||
}
|
||||
sort($allLabels);
|
||||
|
||||
$finalTotalData = [];
|
||||
$finalNodeData = [];
|
||||
$mappedNodeData = [];
|
||||
foreach ($allLabels as $label) {
|
||||
$finalTotalData[] = $totalDataAssoc[$label] ?? 0;
|
||||
foreach ($nodes as $node) {
|
||||
$finalNodeData[$node][] = $nodeDataAssoc[$node][$label] ?? 0;
|
||||
$mappedNodeData[$node][] = $finalNodeData[$node][$label] ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('public_chart', [
|
||||
'allLabels' => $allLabels,
|
||||
'finalTotalData' => $finalTotalData,
|
||||
'finalNodeData' => $finalNodeData
|
||||
'finalNodeData' => $mappedNodeData
|
||||
]);
|
||||
}
|
||||
|
||||
public function backend_request_chart() {
|
||||
$conn = \db_connect();
|
||||
$sql = "SELECT DATE(FIRST_REQUEST) as tag, COUNT(*) as anzahl FROM logs WHERE WEBSITE = 'backend.blackpi.ipv64.net' GROUP BY DATE(FIRST_REQUEST) ORDER BY DATE(FIRST_REQUEST)";
|
||||
$result = $conn->query($sql);
|
||||
|
||||
$data = Log::getChartData("WEBSITE = 'backend.blackpi.ipv64.net'");
|
||||
$labels = [];
|
||||
$data = [];
|
||||
while($row = $result->fetch_assoc()) {
|
||||
$counts = [];
|
||||
foreach ($data as $row) {
|
||||
$labels[] = $row['tag'];
|
||||
$data[] = (int)$row['anzahl'];
|
||||
$counts[] = (int)$row['anzahl'];
|
||||
}
|
||||
|
||||
return $this->render('backend_chart', [
|
||||
'labels' => $labels,
|
||||
'data' => $data
|
||||
'data' => $counts
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,18 @@
|
||||
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';
|
||||
global $Berufsschulberichte;
|
||||
|
||||
if ($mode === 'normal') {
|
||||
$data = \Databaseshow("Firmenberichte");
|
||||
$data = CompanyReport::all();
|
||||
} else {
|
||||
$data = \DatabaseShowWithFK("Berufsschulberichte", $Berufsschulberichte);
|
||||
$data = SchoolReport::allWithFK();
|
||||
}
|
||||
|
||||
$data = array_reverse($data);
|
||||
@@ -30,36 +32,12 @@ class ReportController extends Controller {
|
||||
public function berichte_arbeit_gen() {
|
||||
if (empty($_SESSION['user_logged_in'])) return "Access Denied";
|
||||
|
||||
$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;
|
||||
$wochenArray = CompanyReport::getWochen();
|
||||
|
||||
$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();
|
||||
$berichteArray = CompanyReport::getByDateRange($startDate, $endDate);
|
||||
|
||||
$pdfFile = null;
|
||||
if (isset($_POST['generate_pdf']) && count($berichteArray) > 0) {
|
||||
@@ -120,31 +98,21 @@ class ReportController extends Controller {
|
||||
}
|
||||
|
||||
public function zeiterfassung() {
|
||||
$conn = \db_connect();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_overtime'])) {
|
||||
$datum = $_POST['ot_date'];
|
||||
$minuten = intval($_POST['ot_minutes']);
|
||||
$grund = $conn->real_escape_string($_POST['ot_reason']);
|
||||
\DatabaseInsert("Ueberstunden", "DATUM, MINUTEN, GRUND", $datum, $minuten, $grund);
|
||||
Overtime::insert([
|
||||
'DATUM' => $_POST['ot_date'],
|
||||
'MINUTEN' => (int)$_POST['ot_minutes'],
|
||||
'GRUND' => $_POST['ot_reason']
|
||||
]);
|
||||
\redirectToWebsite((new \getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
$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);
|
||||
$minuten_berichte = CompanyReport::getTotalOvertime();
|
||||
$minuten_abbau = Overtime::getSumByReason('Abbau');
|
||||
$minuten_aufbau = Overtime::getSumByReason('Aufbau');
|
||||
|
||||
$gesamt_minuten = $minuten_berichte - $minuten_abbau + $minuten_aufbau;
|
||||
$conn->close();
|
||||
|
||||
return $this->render('zeiterfassung', [
|
||||
'gesamt_minuten' => $gesamt_minuten,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\Domain;
|
||||
|
||||
class TestController extends Controller {
|
||||
public function api_tester() {
|
||||
@@ -30,19 +31,13 @@ class TestController extends Controller {
|
||||
}
|
||||
|
||||
public function weather_chart() {
|
||||
$conn = \db_connect();
|
||||
$sql = "SELECT ZEIT, AKTUELL FROM Wetter";
|
||||
$result = $conn->query($sql);
|
||||
|
||||
$res = Domain::query("SELECT ZEIT, AKTUELL FROM Wetter");
|
||||
$labels = [];
|
||||
$data = [];
|
||||
if ($result->num_rows > 0) {
|
||||
while($row = $result->fetch_assoc()) {
|
||||
$labels[] = $row['ZEIT'];
|
||||
$data[] = (float)$row['AKTUELL'];
|
||||
}
|
||||
while($row = $res->fetch_assoc()) {
|
||||
$labels[] = $row['ZEIT'];
|
||||
$data[] = (float)$row['AKTUELL'];
|
||||
}
|
||||
$conn->close();
|
||||
|
||||
return $this->render('weather_chart', [
|
||||
'labels' => $labels,
|
||||
@@ -57,26 +52,22 @@ class TestController extends Controller {
|
||||
if (isset($_POST["add_domain"])) {
|
||||
$subdomain = trim($_POST["subdomain"]);
|
||||
if ($subdomain != "") {
|
||||
\DatabaseInsert("Domains", "SUBDOMAIN", $subdomain);
|
||||
Domain::insert(['SUBDOMAIN' => $subdomain]);
|
||||
$statusMsg = "Domain wurde hinzugefügt.";
|
||||
}
|
||||
}
|
||||
if (isset($_POST["delete_domain"])) {
|
||||
$id = intval($_POST["id"]);
|
||||
\DatabaseDelete("Domains", "ID = ?", $id);
|
||||
Domain::delete("ID = ?", intval($_POST["id"]));
|
||||
$statusMsg = "Domain wurde gelöscht.";
|
||||
}
|
||||
if (isset($_POST["save_domain"])) {
|
||||
$id = intval($_POST["id"]);
|
||||
$subdomain = trim($_POST["subdomain"]);
|
||||
\DatabaseUpdate("Domains", "SUBDOMAIN = ?", "ID = ?", $subdomain, $id);
|
||||
Domain::update(['SUBDOMAIN' => trim($_POST["subdomain"])], "ID = ?", intval($_POST["id"]));
|
||||
$statusMsg = "Domain wurde gespeichert.";
|
||||
}
|
||||
}
|
||||
|
||||
$domains = \DatabaseShow("Domains", "*");
|
||||
return $this->render('domains', [
|
||||
'domains' => $domains,
|
||||
'domains' => Domain::all(),
|
||||
'statusMsg' => $statusMsg
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -15,4 +15,11 @@ class Controller {
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Core;
|
||||
|
||||
abstract class Model {
|
||||
protected static $table;
|
||||
|
||||
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 = \db_connect();
|
||||
if (empty($params)) {
|
||||
return $conn->query($sql);
|
||||
}
|
||||
$stmt = $conn->prepare($sql);
|
||||
// This is a simplified version, real projects use more robust parameter binding
|
||||
$types = str_repeat('s', count($params));
|
||||
$stmt->bind_param($types, ...$params);
|
||||
$stmt->execute();
|
||||
return $stmt->get_result();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+98
-102
@@ -1,125 +1,121 @@
|
||||
# 🏗️ Backend MVC Dokumentation
|
||||
# 🏗️ Backend MVC - Umfassende Dokumentation
|
||||
|
||||
Dieses Projekt nutzt eine moderne **Model-View-Controller (MVC)** Struktur mit einem **modularen Block-Layout**. Dies trennt die Geschäftslogik vom Design und macht den Code wartbar und erweiterbar.
|
||||
Diese Dokumentation beschreibt die Architektur, die Funktionsweise und die Besonderheiten des neuen Backend-Systems.
|
||||
|
||||
---
|
||||
|
||||
## 📁 Verzeichnisstruktur
|
||||
## 🏛️ 1. Architektur-Übersicht
|
||||
|
||||
- `WEBROOT/src/MVC/Core/`: Basisklassen (`Controller`, `View`).
|
||||
- `WEBROOT/src/MVC/Controller/`: Hier liegt die Logik (PHP-Klassen).
|
||||
- `WEBROOT/src/MVC/View/`: Hier liegen die Templates (HTML/PHP).
|
||||
- `WEBROOT/src/MVC/Layout/`: Das globale Layout-System und die Blöcke.
|
||||
- `WEBROOT/index.php`: Der "Entry Point", der alles zusammenfügt.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Einen neuen Service erstellen
|
||||
## 🚦 2. Routing & Middleware (Der Verkehrspolizist)
|
||||
|
||||
1. **Dateien anlegen:** Nutze das Script `php create_module.php <name>`.
|
||||
2. **Controller registrieren:** Trage den neuen Controller in der `$controllers`-Liste in `WEBROOT/index.php` ein.
|
||||
3. **In Config eintragen:** Füge den Service in der `config.php` (oder über die Admin-Oberfläche) hinzu, damit er in der Sidebar erscheint.
|
||||
|
||||
---
|
||||
|
||||
## 💡 Arbeiten mit Controller & View
|
||||
|
||||
### Daten vom Controller an die View übergeben
|
||||
Im Controller nutzt du die `render()` Methode:
|
||||
Das Routing wird in der `index.php` über den `Router` konfiguriert.
|
||||
|
||||
### Routen registrieren
|
||||
```php
|
||||
public function mein_service() {
|
||||
$userCount = 42;
|
||||
return $this->render('meine_view', [
|
||||
'anzahl' => $userCount,
|
||||
'status' => 'aktiv'
|
||||
]);
|
||||
}
|
||||
$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']);
|
||||
```
|
||||
|
||||
In der View (`meine_view.php`) sind diese Variablen direkt verfügbar:
|
||||
### 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>Es sind <?= $anzahl ?> Benutzer im Status <?= $status ?>.</p>
|
||||
<p><?= htmlspecialchars($user_name) ?></p>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Mehrere Funktionen in einer View nutzen
|
||||
## 🔍 7. Besonderheiten & Tipps
|
||||
|
||||
Wenn du innerhalb eines Projekts verschiedene Aktionen ausführen willst (z.B. Anzeigen, Bearbeiten, Löschen), gibt es zwei gängige Wege:
|
||||
|
||||
### 1. Über URL-Parameter (Empfohlen für einfache Logik)
|
||||
Du nutzt einen Parameter wie `?action=edit` in deinen Links.
|
||||
|
||||
**Im Controller:**
|
||||
```php
|
||||
public function mein_service() {
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$data = [];
|
||||
|
||||
if ($action === 'edit') {
|
||||
$data = ["mode" => "Bearbeiten"];
|
||||
} else {
|
||||
$data = ["mode" => "Listenansicht"];
|
||||
}
|
||||
|
||||
return $this->render('mein_view', $data);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Formular-Aktionen (POST)
|
||||
Wenn du Buttons für verschiedene Funktionen hast, kannst du diese im Controller abfangen:
|
||||
|
||||
**In der View:**
|
||||
```html
|
||||
<form method="POST">
|
||||
<button type="submit" name="save_data">Speichern</button>
|
||||
<button type="submit" name="delete_data">Löschen</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
**Im Controller:**
|
||||
```php
|
||||
public function mein_service() {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['save_data'])) {
|
||||
// Logik für Speichern
|
||||
}
|
||||
if (isset($_POST['delete_data'])) {
|
||||
// Logik für Löschen
|
||||
}
|
||||
// Nach POST immer Redirect, um "F5-Resubmit" zu verhindern
|
||||
header("Location: ?service=mein_service");
|
||||
exit;
|
||||
}
|
||||
return $this->render('mein_view');
|
||||
}
|
||||
```
|
||||
- **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/`.
|
||||
|
||||
---
|
||||
|
||||
## 🧱 Das Block-Layout System
|
||||
|
||||
Das Layout besteht aus Blöcken. In der `index.php` kannst du diese Blöcke füllen, bevor das Layout gerendert wird:
|
||||
|
||||
- `title`: Der Tab-Titel.
|
||||
- `header`: Der obere Balken.
|
||||
- `sidebar`: Das Seitenmenü.
|
||||
- `content`: Der Hauptinhalt (kommt meist aus dem Controller).
|
||||
- `scripts`: Zusätzliches JavaScript für die Seite.
|
||||
|
||||
Beispiel für zusätzliche Scripte im Controller:
|
||||
```php
|
||||
// In index.php könnte man das Layout erweitern:
|
||||
$layout->setBlock('scripts', '<script src="mein_script.js"></script>');
|
||||
## 🚀 Modul-Generator
|
||||
Nutze das Script im Hauptverzeichnis für schnelle Ergebnisse:
|
||||
```bash
|
||||
php create_module.php mein_name
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Wichtige Regeln (Best Practices)
|
||||
|
||||
1. **Kein HTML im Controller:** Controller bereiten nur Daten vor. HTML gehört ausschließlich in die `View`.
|
||||
2. **Globale Klassen:** Nutze `\` vor globalen Funktionen oder Klassen (z.B. `\DatabaseShow()`), wenn du dich innerhalb eines Namespaces befindest.
|
||||
3. **Security:** Nutze immer `htmlspecialchars()` beim Ausgeben von Variablen in der View, um XSS-Angriffe zu verhindern.
|
||||
4. **Redirects:** Nach einem `POST` (Daten speichern) solltest du immer ein `header("Location: ...")` machen, damit die Seite beim Neuladen die Daten nicht doppelt sendet.
|
||||
5. **Namespaces:** Achte darauf, dass neue Controller immer `namespace MVC\Controller;` am Anfang stehen haben.
|
||||
Dieses erstellt automatisch Controller und View nach den oben genannten Standards.
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Domain extends Model {
|
||||
protected static $table = "Domains";
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
class Variable extends Model {
|
||||
protected static $table = "Variablen";
|
||||
|
||||
public static function getByName($name) {
|
||||
return self::where("VARIABLENNAME = ?", $name)[0] ?? null;
|
||||
}
|
||||
|
||||
public static function toggle($name) {
|
||||
$var = self::getByName($name);
|
||||
if ($var) {
|
||||
$newValue = ($var['WERT'] === "ON") ? "OFF" : "ON";
|
||||
return self::update(['WERT' => $newValue], "VARIABLENNAME = ?", $name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<div class="documentation-container">
|
||||
<h2>📚 Projekt-Dokumentation</h2>
|
||||
|
||||
<div class="search-bar">
|
||||
<form method="GET">
|
||||
<input type="hidden" name="service" value="documentation">
|
||||
<input type="text" name="q" value="<?= htmlspecialchars($query) ?>" placeholder="Suchen...">
|
||||
<button type="submit">Suchen</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="doc-list">
|
||||
<?php if (empty($docs)): ?>
|
||||
<p>Keine Einträge gefunden.</p>
|
||||
<?php else: ?>
|
||||
<?php foreach ($docs as $doc): ?>
|
||||
<div class="doc-item">
|
||||
<span class="category-badge"><?= htmlspecialchars($doc['KATEGORIE']) ?></span>
|
||||
<h3><?= htmlspecialchars($doc['TITEL']) ?></h3>
|
||||
<p><?= nl2br(htmlspecialchars($doc['INHALT'])) ?></p>
|
||||
<small>Erstellt am: <?= date('d.m.Y H:i', strtotime($doc['ERSTELLT_AM'])) ?></small>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($pages > 1): ?>
|
||||
<div class="pagination">
|
||||
<?php for ($i = 1; $i <= $pages; $i++): ?>
|
||||
<a href="?service=documentation&q=<?= urlencode($query) ?>&page=<?= $i ?>"
|
||||
class="<?= $i === $currentPage ? 'active' : '' ?>">
|
||||
<?= $i ?>
|
||||
</a>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.documentation-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.search-bar form {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-bar input {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--input-border);
|
||||
background: var(--input-bg);
|
||||
color: var(--input-text);
|
||||
}
|
||||
|
||||
.doc-item {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.category-badge {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
background: var(--btn-bg);
|
||||
color: white;
|
||||
padding: 4px 10px;
|
||||
border-radius: 15px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.doc-item h3 {
|
||||
margin-top: 0;
|
||||
color: var(--btn-bg);
|
||||
}
|
||||
|
||||
.doc-item p {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.pagination a {
|
||||
display: inline-block;
|
||||
padding: 8px 14px;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.pagination a.active {
|
||||
background: var(--btn-bg);
|
||||
color: white;
|
||||
border-color: var(--btn-bg);
|
||||
}
|
||||
</style>
|
||||
+5
-4
@@ -34,11 +34,12 @@ $controllerContent = <<<PHP
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
// use MVC\Model\YourModel; // Importiere dein Model
|
||||
|
||||
class {$className}Controller extends Controller {
|
||||
public function {$key}() {
|
||||
// Hier kannst du Daten aus der Datenbank laden
|
||||
// \$data = \DatabaseShow("DeineTabelle");
|
||||
// Nutze Models für den Datenbankzugriff
|
||||
// \$data = YourModel::all();
|
||||
|
||||
return \$this->render('{$key}', [
|
||||
'title' => '{$className} Modul',
|
||||
@@ -82,8 +83,8 @@ if (!file_exists($viewFile)) {
|
||||
|
||||
// 3. Anleitung ausgeben
|
||||
echo "\n--- NÄCHSTE SCHRITTE ---\n";
|
||||
echo "1. Registriere den Controller in WEBROOT/index.php im \$controllers Array:\n";
|
||||
echo " '{$key}' => \\MVC\\Controller\\{$className}Controller::class,\n\n";
|
||||
echo "1. Registriere das Modul in WEBROOT/index.php beim Router:\n";
|
||||
echo " \$router->add('{$key}', \\MVC\\Controller\\{$className}Controller::class, '{$key}', ['auth']);\n\n";
|
||||
echo "2. Füge den Service in der Seitenverwaltung (Admin) oder in config.php hinzu:\n";
|
||||
echo " ID: {$key}\n";
|
||||
echo " Name: " . ucfirst($name) . "\n";
|
||||
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
/**
|
||||
* MVC Project Bootstrapper
|
||||
* Erstellt ein neues Projekt-Grundgerüst basierend auf der aktuellen Architektur.
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
die("Dieses Script muss über die CLI ausgeführt werden.\n");
|
||||
}
|
||||
|
||||
$projectName = $argv[1] ?? null;
|
||||
if (!$projectName) {
|
||||
echo "Bitte gib einen Projektnamen an: ";
|
||||
$handle = fopen("php://stdin", "r");
|
||||
$projectName = trim(fgets($handle));
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
if (!$projectName) die("Fehler: Kein Name angegeben.\n");
|
||||
|
||||
$baseDir = getcwd() . DIRECTORY_SEPARATOR . $projectName;
|
||||
|
||||
// 1. Verzeichnisstruktur erstellen
|
||||
$dirs = [
|
||||
'',
|
||||
'WEBROOT',
|
||||
'WEBROOT/static',
|
||||
'WEBROOT/src',
|
||||
'WEBROOT/src/MVC',
|
||||
'WEBROOT/src/MVC/Core',
|
||||
'WEBROOT/src/MVC/Controller',
|
||||
'WEBROOT/src/MVC/Model',
|
||||
'WEBROOT/src/MVC/View',
|
||||
'WEBROOT/src/MVC/Layout',
|
||||
'WEBROOT/src/MVC/Layout/Templates',
|
||||
];
|
||||
|
||||
foreach ($dirs as $dir) {
|
||||
$path = $baseDir . ($dir ? DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $dir) : '');
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0755, true);
|
||||
echo "📁 Ordner erstellt: $dir\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Kern-Dateien definieren
|
||||
$files = [];
|
||||
|
||||
// --- CORE: Controller ---
|
||||
$files['WEBROOT/src/MVC/Core/Controller.php'] = <<<'PHP'
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
|
||||
// --- CORE: View ---
|
||||
$files['WEBROOT/src/MVC/Core/View.php'] = <<<'PHP'
|
||||
<?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.";
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
|
||||
// --- CORE: Model ---
|
||||
$files['WEBROOT/src/MVC/Core/Model.php'] = <<<'PHP'
|
||||
<?php
|
||||
namespace MVC\Core;
|
||||
abstract class Model {
|
||||
protected static $table;
|
||||
public static function all() { /* Implement logic */ return []; }
|
||||
public static function find($id) { /* Implement logic */ return null; }
|
||||
public static function query($sql, ...$params) { /* Implement logic */ return null; }
|
||||
}
|
||||
PHP;
|
||||
|
||||
// --- CORE: Router ---
|
||||
$files['WEBROOT/src/MVC/Core/Router.php'] = <<<'PHP'
|
||||
<?php
|
||||
namespace MVC\Core;
|
||||
class Router {
|
||||
protected $routes = [];
|
||||
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];
|
||||
foreach ($route['middlewares'] as $mw) { $this->runMiddleware($mw); }
|
||||
$controller = new $route['controller']();
|
||||
$method = $route['method'];
|
||||
return $controller->$method();
|
||||
}
|
||||
protected function runMiddleware($mw) {
|
||||
if ($mw === 'auth' && empty($_SESSION['user_logged_in'])) { header('Location: login.php'); exit; }
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
|
||||
// --- CORE: Layout ---
|
||||
$files['WEBROOT/src/MVC/Layout/Layout.php'] = <<<'PHP'
|
||||
<?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 not found.";
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
|
||||
// --- CORE: Autoloader ---
|
||||
$files['WEBROOT/src/MVC/autoloader.php'] = <<<'PHP'
|
||||
<?php
|
||||
spl_autoload_register(function ($class) {
|
||||
$prefix = 'MVC\\'; $base_dir = __DIR__ . '/';
|
||||
$len = strlen($prefix);
|
||||
if (strncmp($prefix, $class, $len) !== 0) return;
|
||||
$file = $base_dir . str_replace('\\', '/', substr($class, $len)) . '.php';
|
||||
if (file_exists($file)) require $file;
|
||||
});
|
||||
PHP;
|
||||
|
||||
// --- LAYOUT TEMPLATE ---
|
||||
$files['WEBROOT/src/MVC/Layout/Templates/main.php'] = <<<'HTML'
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8"><title><?= $this->getBlock('title') ?></title>
|
||||
<style>
|
||||
body { font-family: sans-serif; display: flex; margin: 0; }
|
||||
.sidebar { width: 250px; background: #eee; height: 100vh; padding: 20px; }
|
||||
.content { flex: 1; padding: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="sidebar"><?= $this->getBlock('sidebar') ?></div>
|
||||
<div class="content"><?= $this->getBlock('content') ?></div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
|
||||
// --- DEFAULT CONTROLLER ---
|
||||
$files['WEBROOT/src/MVC/Controller/HomeController.php'] = <<<'PHP'
|
||||
<?php
|
||||
namespace MVC\Controller;
|
||||
use MVC\Core\Controller;
|
||||
class HomeController extends Controller {
|
||||
public function index() {
|
||||
return $this->render('home', ['welcome' => 'Willkommen in deinem neuen MVC Projekt!']);
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
|
||||
// --- DEFAULT VIEW ---
|
||||
$files['WEBROOT/src/MVC/View/home.php'] = <<<'HTML'
|
||||
<h1>Dashboard</h1>
|
||||
<p><?= htmlspecialchars($welcome) ?></p>
|
||||
HTML;
|
||||
|
||||
// --- ENTRY POINT: index.php ---
|
||||
$files['WEBROOT/index.php'] = <<<'PHP'
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/src/MVC/autoloader.php';
|
||||
|
||||
$service = $_GET['service'] ?? 'home';
|
||||
|
||||
try {
|
||||
$router = new \MVC\Core\Router();
|
||||
$router->add('home', \MVC\Controller\HomeController::class, 'index');
|
||||
|
||||
$layout = new \MVC\Layout\Layout('main');
|
||||
$layout->setBlock('title', 'Mein Neues Projekt');
|
||||
$layout->setBlock('sidebar', '<a href="?service=home">Home</a>');
|
||||
$layout->setBlock('content', $router->dispatch($service));
|
||||
|
||||
echo $layout->render();
|
||||
} catch (\Exception $e) {
|
||||
echo "<h1>Fehler</h1><p>" . $e->getMessage() . "</p>";
|
||||
}
|
||||
PHP;
|
||||
|
||||
// 3. Dateien schreiben
|
||||
foreach ($files as $file => $content) {
|
||||
file_put_contents($baseDir . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $file), $content);
|
||||
echo "📄 Datei erstellt: $file\n";
|
||||
}
|
||||
|
||||
echo "\n✨ Projekt '$projectName' wurde erfolgreich initialisiert!\n";
|
||||
echo "Gehe in den Ordner und starte deinen Webserver in WEBROOT.\n";
|
||||
PHP;
|
||||
Reference in new issue
Block a user