Files
domili/version.php
2025-11-21 11:45:10 +01:00

433 lines
22 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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'); ?>