433 lines
22 KiB
PHP
Executable File
433 lines
22 KiB
PHP
Executable File
<?php
|
||
include('inc/check_login.php');
|
||
require_once('inc/db.php');
|
||
|
||
define('APP_URL', 'https://domili.borgal.de');
|
||
|
||
// 🔹 Funktion zur sicheren Formatierung
|
||
function parse_formatting($text)
|
||
{
|
||
if (empty($text)) return '';
|
||
$text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
|
||
$text = preg_replace('/\[b\](.*?)\[\/b\]/i', '<strong>$1</strong>', $text);
|
||
$text = preg_replace('/\[i\](.*?)\[\/i\]/i', '<em>$1</em>', $text);
|
||
return $text;
|
||
}
|
||
|
||
// 🔹 Holt alle Benutzer mit gültiger E-Mail
|
||
function get_all_user_emails($conn)
|
||
{
|
||
$stmt = mysqli_prepare($conn, "SELECT id, username, email FROM users WHERE email IS NOT NULL AND email != ''");
|
||
mysqli_stmt_execute($stmt);
|
||
$result = mysqli_stmt_get_result($stmt);
|
||
$users = [];
|
||
while ($row = mysqli_fetch_assoc($result)) {
|
||
$users[] = $row;
|
||
}
|
||
mysqli_stmt_close($stmt);
|
||
return $users;
|
||
}
|
||
|
||
$is_admin = ($_SESSION['role'] === 'admin');
|
||
$message = '';
|
||
$message_type = '';
|
||
$edit_mode = false;
|
||
$edit_release = null;
|
||
|
||
if ($is_admin) {
|
||
if (isset($_GET['action']) && $_GET['action'] === 'publish' && isset($_GET['id'])) {
|
||
$id = (int)$_GET['id'];
|
||
|
||
$stmt_data = mysqli_prepare($conn, "SELECT version, release_date, features, bugfixes, improvements FROM releases WHERE id = ?");
|
||
mysqli_stmt_bind_param($stmt_data, "i", $id);
|
||
mysqli_stmt_execute($stmt_data);
|
||
$release_data = mysqli_fetch_assoc(mysqli_stmt_get_result($stmt_data));
|
||
mysqli_stmt_close($stmt_data);
|
||
|
||
if (!$release_data) {
|
||
$_SESSION['message'] = "Version nicht gefunden.";
|
||
$_SESSION['message_type'] = 'danger';
|
||
header("Location: version.php");
|
||
exit();
|
||
}
|
||
|
||
$stmt = mysqli_prepare($conn, "UPDATE releases SET is_draft = 0 WHERE id = ?");
|
||
mysqli_stmt_bind_param($stmt, "i", $id);
|
||
$success = mysqli_stmt_execute($stmt);
|
||
mysqli_stmt_close($stmt);
|
||
|
||
if ($success) {
|
||
$all_users = get_all_user_emails($conn);
|
||
|
||
if (!empty($all_users) && file_exists(__DIR__ . '/vendor/autoload.php')) {
|
||
require_once __DIR__ . '/vendor/autoload.php';
|
||
|
||
$version = htmlspecialchars($release_data['version']);
|
||
$release_date_raw = date('d.m.Y', strtotime($release_data['release_date']));
|
||
$version_url = APP_URL . '/version.php';
|
||
$app_url = APP_URL;
|
||
|
||
$features = array_filter(array_map('trim', explode("\n", $release_data['features'] ?? '')));
|
||
$bugs = array_filter(array_map('trim', explode("\n", $release_data['bugfixes'] ?? '')));
|
||
$improvements = array_filter(array_map('trim', explode("\n", $release_data['improvements'] ?? '')));
|
||
|
||
foreach ($all_users as $user) {
|
||
$recipient_email = $user['email'];
|
||
$recipient_name = $user['username'] ?? 'Benutzer';
|
||
|
||
try {
|
||
$mail = new \PHPMailer\PHPMailer\PHPMailer(true);
|
||
$mail->CharSet = 'UTF-8';
|
||
$mail->isSMTP();
|
||
$mail->Host = SMTP_HOST;
|
||
$mail->SMTPAuth = true;
|
||
$mail->Username = SMTP_USERNAME;
|
||
$mail->Password = SMTP_PASSWORD;
|
||
$mail->SMTPSecure = SMTP_ENCRYPTION;
|
||
$mail->Port = SMTP_PORT;
|
||
$mail->setFrom(MAIL_FROM_ADDRESS, MAIL_FROM_NAME);
|
||
|
||
// Text-Version
|
||
$text_body = "Hallo $recipient_name,\n\n";
|
||
$text_body .= "Es wurde soeben eine neue Version in DoMiLi veröffentlicht:\n\n";
|
||
$text_body .= "Version: $version\n";
|
||
$text_body .= "Veröffentlicht am: $release_date_raw\n";
|
||
$text_body .= "Details: $version_url\n\n";
|
||
|
||
if (!empty($features)) {
|
||
$text_body .= "Neue Features:\n";
|
||
foreach ($features as $f) {
|
||
$text_body .= "- " . strip_tags(parse_formatting($f)) . "\n";
|
||
}
|
||
$text_body .= "\n";
|
||
}
|
||
if (!empty($improvements)) {
|
||
$text_body .= "Verbesserungen:\n";
|
||
foreach ($improvements as $i) {
|
||
$text_body .= "- " . strip_tags(parse_formatting($i)) . "\n";
|
||
}
|
||
$text_body .= "\n";
|
||
}
|
||
if (!empty($bugs)) {
|
||
$text_body .= "Behobene Fehler:\n";
|
||
foreach ($bugs as $b) {
|
||
$text_body .= "- " . strip_tags(parse_formatting($b)) . "\n";
|
||
}
|
||
$text_body .= "\n";
|
||
}
|
||
|
||
$text_body .= "Direkt zur App: $app_url\n\n";
|
||
$text_body .= "Dein DoMiLi-Admin";
|
||
|
||
// HTML-Version
|
||
$html_features = '';
|
||
if (!empty($features)) {
|
||
$html_features .= "<p style=\"margin: 0.5em 0;\"><strong>Neue Features:</strong></p>\n<ul style=\"margin-top: 0.5em; margin-bottom: 0.5em;\">";
|
||
foreach ($features as $f) {
|
||
$html_features .= "<li>" . parse_formatting($f) . "</li>\n";
|
||
}
|
||
$html_features .= "</ul>\n";
|
||
}
|
||
|
||
$html_improvements = '';
|
||
if (!empty($improvements)) {
|
||
$html_improvements .= "<p style=\"margin: 0.5em 0;\"><strong>Verbesserungen:</strong></p>\n<ul style=\"margin-top: 0.5em; margin-bottom: 0.5em;\">";
|
||
foreach ($improvements as $i) {
|
||
$html_improvements .= "<li>" . parse_formatting($i) . "</li>\n";
|
||
}
|
||
$html_improvements .= "</ul>\n";
|
||
}
|
||
|
||
$html_bugs = '';
|
||
if (!empty($bugs)) {
|
||
$html_bugs .= "<p style=\"margin: 0.5em 0;\"><strong>Behobene Fehler:</strong></p>\n<ul style=\"margin-top: 0.5em; margin-bottom: 0.5em;\">";
|
||
foreach ($bugs as $b) {
|
||
$html_bugs .= "<li>" . parse_formatting($b) . "</li>\n";
|
||
}
|
||
$html_bugs .= "</ul>\n";
|
||
}
|
||
|
||
$html_body = "
|
||
<p>Hallo <strong>$recipient_name</strong>,</p>
|
||
<p>Es wurde soeben eine neue Version in DoMiLi veröffentlicht:</p>
|
||
<ul>
|
||
<li><strong>Version:</strong> <a href=\"$version_url\" style=\"color: #0d6efd; text-decoration: none;\">$version</a></li>
|
||
<li><strong>Veröffentlicht am:</strong>
|
||
<span x-apple-data-detectors=\"false\" style=\"display: inline-block;\">$release_date_raw</span>
|
||
</li>
|
||
</ul>
|
||
$html_features
|
||
$html_improvements
|
||
$html_bugs
|
||
<p><a href=\"$app_url\" style=\"color: #0d6efd; text-decoration: none;\">Direkt zur App</a></p>
|
||
<p><em>Dein DoMiLi-Admin</em></p>
|
||
";
|
||
|
||
$mail->isHTML(true);
|
||
$mail->Subject = "DoMiLi: Neue Version veröffentlicht – " . $release_data['version'];
|
||
$mail->Body = $html_body;
|
||
$mail->AltBody = $text_body;
|
||
$mail->addAddress($recipient_email);
|
||
$mail->send();
|
||
} catch (Exception $e) {
|
||
error_log("PHPMailer Fehler beim Senden an $recipient_email: " . $mail->ErrorInfo);
|
||
}
|
||
}
|
||
}
|
||
|
||
$_SESSION['message'] = "Version veröffentlicht!";
|
||
$_SESSION['message_type'] = 'success';
|
||
} else {
|
||
$_SESSION['message'] = "Fehler beim Freigeben.";
|
||
$_SESSION['message_type'] = 'danger';
|
||
}
|
||
header("Location: version.php");
|
||
exit();
|
||
}
|
||
|
||
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
|
||
$id = (int)$_GET['id'];
|
||
$stmt = mysqli_prepare($conn, "DELETE FROM releases WHERE id = ?");
|
||
mysqli_stmt_bind_param($stmt, "i", $id);
|
||
if (mysqli_stmt_execute($stmt)) {
|
||
$_SESSION['message'] = "Version gelöscht!";
|
||
$_SESSION['message_type'] = 'success';
|
||
} else {
|
||
$_SESSION['message'] = "Fehler beim Löschen.";
|
||
$_SESSION['message_type'] = 'danger';
|
||
}
|
||
mysqli_stmt_close($stmt);
|
||
header("Location: version.php");
|
||
exit();
|
||
}
|
||
|
||
if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) {
|
||
$id = (int)$_GET['id'];
|
||
$stmt = mysqli_prepare($conn, "SELECT id, version, release_date, features, bugfixes, improvements, is_draft FROM releases WHERE id = ?");
|
||
mysqli_stmt_bind_param($stmt, "i", $id);
|
||
mysqli_stmt_execute($stmt);
|
||
$edit_release = mysqli_fetch_assoc(mysqli_stmt_get_result($stmt));
|
||
mysqli_stmt_close($stmt);
|
||
$edit_mode = true;
|
||
if (!$edit_release) {
|
||
$_SESSION['message'] = "Version nicht gefunden.";
|
||
$_SESSION['message_type'] = 'warning';
|
||
}
|
||
}
|
||
|
||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||
$version = trim($_POST['version'] ?? '');
|
||
$date = trim($_POST['date'] ?? '');
|
||
$features = trim($_POST['features'] ?? '');
|
||
$bugfixes = trim($_POST['bugfixes'] ?? '');
|
||
$improvements = trim($_POST['improvements'] ?? '');
|
||
$id = !empty($_POST['id']) ? (int)$_POST['id'] : null;
|
||
|
||
if (empty($version) || empty($date)) {
|
||
$_SESSION['message'] = "Versionsnummer und Datum sind erforderlich.";
|
||
$_SESSION['message_type'] = 'danger';
|
||
} elseif (!preg_match('/^v\d+\.\d+\.\d+$/', $version)) {
|
||
$_SESSION['message'] = "Ungültiges Versionsformat. Beispiel: v1.4.2";
|
||
$_SESSION['message_type'] = 'danger';
|
||
} elseif (!strtotime($date)) {
|
||
$_SESSION['message'] = "Ungültiges Datum.";
|
||
$_SESSION['message_type'] = 'danger';
|
||
} else {
|
||
if ($id) {
|
||
$stmt = mysqli_prepare($conn, "UPDATE releases SET version = ?, release_date = ?, features = ?, bugfixes = ?, improvements = ? WHERE id = ?");
|
||
mysqli_stmt_bind_param($stmt, "sssssi", $version, $date, $features, $bugfixes, $improvements, $id);
|
||
} else {
|
||
$stmt = mysqli_prepare($conn, "INSERT INTO releases (version, release_date, features, bugfixes, improvements, is_draft) VALUES (?, ?, ?, ?, ?, 1)");
|
||
mysqli_stmt_bind_param($stmt, "sssss", $version, $date, $features, $bugfixes, $improvements);
|
||
}
|
||
|
||
if (mysqli_stmt_execute($stmt)) {
|
||
$_SESSION['message'] = $id ? "Version aktualisiert!" : "Entwurf gespeichert!";
|
||
$_SESSION['message_type'] = 'success';
|
||
mysqli_stmt_close($stmt);
|
||
header("Location: version.php");
|
||
exit();
|
||
} else {
|
||
$_SESSION['message'] = "Fehler beim Speichern.";
|
||
$_SESSION['message_type'] = 'danger';
|
||
}
|
||
mysqli_stmt_close($stmt);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (isset($_SESSION['message'])) {
|
||
$message = $_SESSION['message'];
|
||
$message_type = $_SESSION['message_type'];
|
||
unset($_SESSION['message'], $_SESSION['message_type']);
|
||
}
|
||
|
||
if ($is_admin) {
|
||
$result = mysqli_query($conn, "SELECT id, version, release_date, features, bugfixes, improvements, is_draft FROM releases ORDER BY release_date DESC, id DESC");
|
||
} else {
|
||
$result = mysqli_query($conn, "SELECT id, version, release_date, features, bugfixes, improvements FROM releases WHERE is_draft = 0 ORDER BY release_date DESC, id DESC");
|
||
}
|
||
|
||
$releases = [];
|
||
while ($row = mysqli_fetch_assoc($result)) {
|
||
$row['features_list'] = array_filter(array_map('trim', explode("\n", $row['features'] ?? '')));
|
||
$row['bugfixes_list'] = array_filter(array_map('trim', explode("\n", $row['bugfixes'] ?? '')));
|
||
$row['improvements_list'] = array_filter(array_map('trim', explode("\n", $row['improvements'] ?? '')));
|
||
$releases[] = $row;
|
||
}
|
||
|
||
$latest_published_version = null;
|
||
foreach ($releases as $rel) {
|
||
if (($rel['is_draft'] ?? 0) == 0) {
|
||
$latest_published_version = $rel['version'];
|
||
break;
|
||
}
|
||
}
|
||
|
||
require_once('inc/header.php');
|
||
?>
|
||
|
||
<!-- 🔸 ANGEPASST: Kein row/col-Wrapper – direkter container wie auf anderen Seiten -->
|
||
<div class="container mt-5 mb-4">
|
||
<?php if ($message): ?>
|
||
<div class="alert alert-<?= htmlspecialchars($message_type) ?> alert-dismissible fade show" role="alert">
|
||
<?= htmlspecialchars($message) ?>
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<h2 class="mb-4">📋 Versionsübersicht</h2>
|
||
|
||
<div class="card shadow">
|
||
<div class="card-header bg-primary-subtle text-secondary d-flex justify-content-between align-items-center">
|
||
<h4 class="mb-0">Release Notes</h4>
|
||
<?php if ($is_admin): ?>
|
||
<a class="btn btn-sm d-flex align-items-center justify-content-center" data-bs-toggle="collapse" href="#releaseFormCollapse" role="button" aria-expanded="false" aria-controls="releaseFormCollapse">Add
|
||
<span class="material-symbols-outlined">add</span>
|
||
</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="card-body">
|
||
|
||
<?php if ($is_admin): ?>
|
||
<div class="collapse <?= $edit_mode ? 'show' : '' ?>" id="releaseFormCollapse">
|
||
<div class="card card-body bg-light mb-4">
|
||
<h5><?= $edit_mode ? 'Version bearbeiten' : 'Neuen Entwurf anlegen'; ?></h5>
|
||
<form method="POST">
|
||
<?php if ($edit_mode): ?>
|
||
<input type="hidden" name="id" value="<?= htmlspecialchars($edit_release['id']); ?>">
|
||
<?php endif; ?>
|
||
<div class="mb-3">
|
||
<label class="form-label">Versionsnummer</label>
|
||
<input type="text" class="form-control" name="version" value="<?= htmlspecialchars($edit_release['version'] ?? ''); ?>" placeholder="z. B. v1.5.0" required>
|
||
<div class="form-text">Format: v1.4.2</div>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Veröffentlichungsdatum</label>
|
||
<input type="date" class="form-control" name="date" value="<?= htmlspecialchars($edit_release['release_date'] ?? date('Y-m-d')); ?>" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Neue Features (ein Punkt pro Zeile)<br>
|
||
<small class="text-muted">Verwende [b]fett[/b] oder [i]kursiv[/i] für Hervorhebungen.</small>
|
||
</label>
|
||
<textarea class="form-control" name="features" rows="4"><?= htmlspecialchars($edit_release['features'] ?? '') ?></textarea>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Verbesserungen (ein Punkt pro Zeile)<br>
|
||
<small class="text-muted">Verwende [b]fett[/b] oder [i]kursiv[/i] für Hervorhebungen.</small>
|
||
</label>
|
||
<textarea class="form-control" name="improvements" rows="4"><?= htmlspecialchars($edit_release['improvements'] ?? '') ?></textarea>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Behobene Fehler (ein Punkt pro Zeile)<br>
|
||
<small class="text-muted">Verwende [b]fett[/b] oder [i]kursiv[/i] für Hervorhebungen.</small>
|
||
</label>
|
||
<textarea class="form-control" name="bugfixes" rows="4"><?= htmlspecialchars($edit_release['bugfixes'] ?? '') ?></textarea>
|
||
</div>
|
||
<div class="d-flex gap-2">
|
||
<button type="submit" class="btn btn-sm btn-outline-<?= $edit_mode ? 'success' : 'primary'; ?>"><?= $edit_mode ? 'Speichern' : 'Entwurf speichern'; ?></button>
|
||
<a href="version.php" class="btn btn-sm btn-outline-secondary">Abbrechen</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (empty($releases)): ?>
|
||
<p class="text-muted">Keine veröffentlichten Release Notes vorhanden.</p>
|
||
<?php else: ?>
|
||
<?php foreach ($releases as $release): ?>
|
||
<div class="d-flex align-items-start">
|
||
<div class="flex-grow-1">
|
||
<h5 class="mt-4 mb-1">
|
||
<?= htmlspecialchars($release['version']) ?>
|
||
<?php if (($release['is_draft'] ?? 0) == 1): ?>
|
||
<span class="badge bg-warning text-dark ms-1" style="font-size: 0.65em; font-weight: normal; padding: 0.2em 0.4em;">Entwurf</span>
|
||
<?php elseif ($release['version'] === $latest_published_version): ?>
|
||
<span class="badge bg-white text-primary border border-primary ms-1" style="font-size: 0.65em; font-weight: normal; padding: 0.15em 0.3em; position: relative; top: -0.7px;">aktuelle Version</span>
|
||
<?php endif; ?>
|
||
</h5>
|
||
<p class="text-muted small mb-3">Veröffentlicht am: <?= date('d.m.Y', strtotime($release['release_date'])) ?></p>
|
||
|
||
<?php if (!empty($release['features_list'])): ?>
|
||
<p class="small text-success mb-1"><strong>Neue Features:</strong></p>
|
||
<ul class="mb-3">
|
||
<?php foreach ($release['features_list'] as $f): ?>
|
||
<li><?= parse_formatting($f) ?></li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!empty($release['improvements_list'])): ?>
|
||
<p class="small text-info mb-1"><strong>Verbesserungen:</strong></p>
|
||
<ul class="mb-3">
|
||
<?php foreach ($release['improvements_list'] as $i): ?>
|
||
<li><?= parse_formatting($i) ?></li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!empty($release['bugfixes_list'])): ?>
|
||
<p class="small text-danger mb-1"><strong>Behobene Fehler:</strong></p>
|
||
<ul class="mb-3">
|
||
<?php foreach ($release['bugfixes_list'] as $b): ?>
|
||
<li><?= parse_formatting($b) ?></li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php if ($is_admin): ?>
|
||
<div class="dropdown ms-3 mt-4">
|
||
<a href="#" class="text-secondary" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||
<span class="material-icons">more_vert</span>
|
||
</a>
|
||
<ul class="dropdown-menu dropdown-menu-end">
|
||
<?php if (($release['is_draft'] ?? 0) == 1): ?>
|
||
<li>
|
||
<a class="dropdown-item d-flex align-items-center text-success" href="version.php?action=publish&id=<?= $release['id'] ?>" onclick="return confirm('Wirklich veröffentlichen?')">
|
||
<span class="material-icons me-2">check</span> Veröffentlichen
|
||
</a>
|
||
</li>
|
||
<?php endif; ?>
|
||
<li>
|
||
<a class="dropdown-item d-flex align-items-center" href="version.php?action=edit&id=<?= $release['id'] ?>">
|
||
<span class="material-icons me-2">mode_edit_outline</span> Bearbeiten
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="dropdown-item d-flex align-items-center text-danger" href="version.php?action=delete&id=<?= $release['id'] ?>" onclick="return confirm('Wirklich löschen?')">
|
||
<span class="material-icons me-2">delete_outline</span> Löschen
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php include('inc/footer.php'); ?>
|