Place Holder for missing Images

This commit is contained in:
Hein Erik
2026-04-08 13:15:54 +02:00
parent d648b3bdd0
commit 3942105cda
3 changed files with 116 additions and 38 deletions

145
admin.php
View File

@@ -1,8 +1,8 @@
<?php
// admin.php - Multi-User Login, Google Authenticator 2FA & News Verwaltung
// admin.php - Multi-User Login, Google Authenticator 2FA, Cropper, News Verwaltung & AES-256 VERSCHLÜSSELUNG
session_start();
// Fehleranzeige (für Live-Betrieb auf 0 setzen)
// Fehleranzeige (für Live-Betrieb auf 0 setzen - oder anlassen, wenn du Schmerzen magst)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
@@ -13,6 +13,7 @@ error_reporting(E_ALL);
class MiniTOTP {
public static function verify($secret, $code) {
$time = floor(time() / 30);
// Prüft aktuelles, vorheriges und nächstes Zeitfenster (Toleranz)
for ($i = -1; $i <= 1; $i++) {
if (self::getCode($secret, $time + $i) === $code) return true;
}
@@ -156,43 +157,41 @@ if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
// ============================================================================
$message = "";
// --- NEU: Bild-Upload Funktion ---
// --- BILD-UPLOAD FUNKTION (mit Base64 Crop-Unterstützung) ---
function handleImageUpload() {
if (isset($_FILES['image_file']) && $_FILES['image_file']['error'] === UPLOAD_ERR_OK) {
if (!empty($_POST['cropped_image_data'])) {
$data = $_POST['cropped_image_data'];
list($type, $data) = explode(';', $data);
list(, $data) = explode(',', $data);
$data = base64_decode($data);
$year = date('Y');
// Ordnerstruktur: img/news/YYYY (wird automatisch erstellt, falls nicht vorhanden)
$upload_dir = __DIR__ . '/img/news/' . $year;
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0777, true);
mkdir($upload_dir, 0777, true); // Erstellt den Ordner, wenn er fehlt
}
$ext = strtolower(pathinfo($_FILES['image_file']['name'], PATHINFO_EXTENSION));
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$filename = date('Ymd') . '_' . rand(10000, 99999) . '.jpg';
$target_file = $upload_dir . '/' . $filename;
if (in_array($ext, $allowed)) {
// Format: YYYYMMDD_randomnummer.format
$filename = date('Ymd') . '_' . rand(10000, 99999) . '.' . $ext;
$target_file = $upload_dir . '/' . $filename;
if (move_uploaded_file($_FILES['image_file']['tmp_name'], $target_file)) {
// Gib den relativen Pfad für die Datenbank zurück
return 'img/news/' . $year . '/' . $filename;
}
if (file_put_contents($target_file, $data)) {
return 'img/news/' . $year . '/' . $filename;
}
}
return null;
}
// Aktion: News aktualisieren (EDIT)
// --- AKTION: NEWS AKTUALISIEREN (EDIT) ---
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_news'])) {
$final_image_path = $_POST['existing_image']; // Standard: Altes Bild behalten
$final_image_path = $_POST['existing_image'];
$uploaded_img = handleImageUpload();
if ($uploaded_img) {
$final_image_path = $uploaded_img; // Neues Upload-Bild nutzen
} elseif (isset($_POST['image_url'])) {
// Link korrigieren, falls manuell etwas eingetragen wurde
$final_image_path = $uploaded_img;
} elseif (isset($_POST['image_url']) && !empty($_POST['image_url'])) {
$final_image_path = str_replace(['http://new.sg07-stleon.de/', 'https://new.sg07-stleon.de/', 'new.sg07-stleon.de/', 'new.sg07-stleon.de\\'], '', trim($_POST['image_url']));
} elseif (isset($_POST['placeholder_image']) && !empty($_POST['placeholder_image'])) {
$final_image_path = $_POST['placeholder_image'];
}
$stmt = $db->prepare("UPDATE news SET date=?, department=?, title=?, content=?, full_text=?, image=?, link=? WHERE id=?");
@@ -201,15 +200,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_news'])) {
}
}
// Aktion: News speichern (NEU ERSTELLEN)
// --- AKTION: NEWS SPEICHERN (NEU) ---
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_news'])) {
$final_image_path = '';
$uploaded_img = handleImageUpload();
if ($uploaded_img) {
$final_image_path = $uploaded_img;
} else {
$final_image_path = str_replace(['http://new.sg07-stleon.de/', 'https://new.sg07-stleon.de/', 'new.sg07-stleon.de/', 'new.sg07-stleon.de\\'], '', trim($_POST['image_url'] ?? ''));
} elseif (isset($_POST['image_url']) && !empty($_POST['image_url'])) {
$final_image_path = str_replace(['http://new.sg07-stleon.de/', 'https://new.sg07-stleon.de/', 'new.sg07-stleon.de/', 'new.sg07-stleon.de\\'], '', trim($_POST['image_url']));
} elseif (isset($_POST['placeholder_image']) && !empty($_POST['placeholder_image'])) {
$final_image_path = $_POST['placeholder_image'];
}
$stmt = $db->prepare("INSERT INTO news (date, department, title, content, full_text, image, link) VALUES (?, ?, ?, ?, ?, ?, ?)");
@@ -218,7 +219,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_news'])) {
}
}
// Aktion: News löschen
// --- AKTION: NEWS LÖSCHEN ---
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_news'])) {
$stmt = $db->prepare("DELETE FROM news WHERE id = ?");
if ($stmt->execute([$_POST['delete_id']])) {
@@ -226,7 +227,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_news'])) {
}
}
// Aktion: Neuen Benutzer anlegen
// --- AKTION: NEUEN BENUTZER ANLEGEN ---
$new_user_qr = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_user'])) {
$new_user = trim($_POST['new_username']);
@@ -243,17 +244,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_user'])) {
}
}
// Edit-Modus prüfen (Ist ein Klick auf "Ändern" erfolgt?)
// Edit-Modus prüfen
$is_edit = false;
$edit_news = null;
if (isset($_GET['edit_id'])) {
$stmt = $db->prepare("SELECT * FROM news WHERE id = ?");
$stmt->execute([$_GET['edit_id']]);
$edit_news = $stmt->fetch(PDO::FETCH_ASSOC);
if ($edit_news) $is_edit = true;
if ($edit_news) {
$is_edit = true;
}
}
// Alle bestehenden News abrufen, um sie aufzulisten
// Alle bestehenden News abrufen für die Übersichtstabelle
$existing_news = $db->query("SELECT id, date, department, title FROM news ORDER BY id DESC")->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
@@ -261,6 +264,9 @@ $existing_news = $db->query("SELECT id, date, department, title FROM news ORDER
<head>
<meta charset="UTF-8">
<title>Admin Dashboard | SG 07 St. Leon</title>
<!-- Cropper.js für Bildzuschnitt -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js"></script>
<style>
body { font-family: 'Segoe UI', sans-serif; background: #f4f4f4; padding: 40px 20px; margin: 0; }
.container { max-width: 1000px; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); margin: 0 auto; }
@@ -277,12 +283,12 @@ $existing_news = $db->query("SELECT id, date, department, title FROM news ORDER
.sidebar-box { background: #fafafa; padding: 20px; border-radius: 8px; border: 1px solid #ddd; }
.qr-box { background: #fff3cd; color: #856404; padding: 20px; border-radius: 8px; text-align: center; margin-bottom: 20px; border: 1px solid #ffeeba;}
/* Tabelle für News Verwaltung */
.news-table { width: 100%; border-collapse: collapse; margin-top: 20px; font-size: 0.95rem; }
.news-table th { background: #f4f4f4; padding: 12px; text-align: left; border-bottom: 2px solid #ddd; color: #333; }
.news-table td { padding: 12px; border-bottom: 1px solid #eee; vertical-align: middle; }
.news-table tr:hover { background: #fafafa; }
.edit-btn { background: #2196F3; color: white; padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; font-size: 0.9rem; }
.social-btn { background: #E1306C; color: white; padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; font-size: 0.9rem; margin-right: 5px; }
.delete-btn { background: #d32f2f; color: white; padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; margin: 0; width: auto; font-size: 0.9rem; }
@media(max-width: 768px) { .grid-2 { grid-template-columns: 1fr; } }
@@ -305,7 +311,7 @@ $existing_news = $db->query("SELECT id, date, department, title FROM news ORDER
<!-- News Eintragen / Ändern -->
<div>
<h2 style="color: #333; margin-bottom: 10px;"><?= $is_edit ? 'News bearbeiten' : 'News veröffentlichen' ?></h2>
<form method="POST" enctype="multipart/form-data">
<form method="POST" id="newsForm" enctype="multipart/form-data">
<?php if($is_edit): ?>
<input type="hidden" name="news_id" value="<?= $edit_news['id'] ?>">
<input type="hidden" name="existing_image" value="<?= htmlspecialchars($edit_news['image']) ?>">
@@ -322,8 +328,15 @@ $existing_news = $db->query("SELECT id, date, department, title FROM news ORDER
<label>Kompletter Artikel (Popup)</label>
<textarea name="full_text" rows="8" required><?= $is_edit ? htmlspecialchars($edit_news['full_text']) : '' ?></textarea>
<label>Bild hochladen (wird gespeichert unter /img/news/Jahr/...)</label>
<input type="file" name="image_file" accept="image/*" style="background: white;">
<label>Bild hochladen & zuschneiden</label>
<input type="file" id="image_file_input" accept="image/*" style="background: white; margin-bottom: 10px;">
<!-- Cropper Container -->
<div id="cropper_container" style="display:none; margin-bottom: 15px; max-height: 400px; overflow: hidden; border: 1px dashed #ccc;">
<img id="image_to_crop" src="" style="max-width: 100%; display: block;">
</div>
<input type="hidden" name="cropped_image_data" id="cropped_image_data">
<?php if($is_edit && $edit_news['image']): ?>
<p style="font-size: 0.85rem; color: #666; margin-top: -10px; margin-bottom: 10px;">Aktuelles Bild: <b><?= htmlspecialchars($edit_news['image']) ?></b></p>
<?php endif; ?>
@@ -331,6 +344,14 @@ $existing_news = $db->query("SELECT id, date, department, title FROM news ORDER
<label>ODER Bild-Link (URL)</label>
<input type="text" name="image_url" placeholder="https://..." value="<?= $is_edit ? htmlspecialchars($edit_news['image']) : '' ?>">
<label>ODER Platzhalter-Bild verwenden</label>
<select name="placeholder_image" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-family: inherit; margin-bottom: 15px; background: white;">
<option value="">-- Kein Platzhalter (Leeres Bild) --</option>
<option value="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' fill='%23f4f4f4'/%3E%3Ccircle cx='50' cy='50' r='35' fill='%23d32f2f'/%3E%3Ctext x='50' y='57' font-size='20' fill='white' text-anchor='middle' font-weight='bold'%3ESG 07%3C/text%3E%3C/svg%3E">Neutral (SG 07 Logo)</option>
<option value="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' fill='%23e3f2fd'/%3E%3Ccircle cx='50' cy='50' r='35' fill='%232196f3'/%3E%3Ctext x='50' y='60' font-size='30' fill='white' text-anchor='middle' font-weight='bold' font-family='serif'%3Ei%3C/text%3E%3C/svg%3E">Info (Blaues i)</option>
<option value="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' fill='%23fff3e0'/%3E%3Cpath d='M50 20 L85 80 L15 80 Z' fill='%23ff9800'/%3E%3Ctext x='50' y='70' font-size='30' fill='white' text-anchor='middle' font-weight='bold'%3E!%3C/text%3E%3C/svg%3E">Wichtig (Warnung)</option>
</select>
<label>Weiterführender Link (Optional)</label>
<input type="text" name="link" value="<?= $is_edit ? htmlspecialchars($edit_news['link']) : '' ?>">
@@ -361,17 +382,16 @@ $existing_news = $db->query("SELECT id, date, department, title FROM news ORDER
</div>
</div>
<!-- News Liste zum Löschen -->
<!-- News Liste zum Löschen / Posten / Ändern -->
<div style="margin-top: 50px; border-top: 2px solid #eee; padding-top: 30px;">
<h2 style="color: #333; margin-bottom: 10px;">Bestehende News verwalten</h2>
<p style="color: #666; font-size: 0.95rem;">Hier kannst du fehlerhafte oder veraltete News endgültig von der Website entfernen.</p>
<table class="news-table">
<tr>
<th>Datum</th>
<th>Abteilung</th>
<th>Titel</th>
<th style="width: 100px; text-align: right;">Aktion</th>
<th style="width: 250px; text-align: right;">Aktion</th>
</tr>
<?php foreach($existing_news as $news_item): ?>
<tr>
@@ -379,6 +399,7 @@ $existing_news = $db->query("SELECT id, date, department, title FROM news ORDER
<td><span style="background: #eee; padding: 2px 8px; border-radius: 4px; font-size: 0.85rem;"><?= htmlspecialchars($news_item['department']) ?></span></td>
<td><strong><?= htmlspecialchars($news_item['title']) ?></strong></td>
<td style="text-align: right; display: flex; justify-content: flex-end; gap: 5px;">
<button type="button" class="social-btn" onclick="copySocialMediaText('<?= htmlspecialchars(addslashes($news_item['title'])) ?>', '<?= htmlspecialchars(addslashes($news_item['department'])) ?>')">📲 Posten</button>
<a href="?edit_id=<?= $news_item['id'] ?>" class="edit-btn">Ändern</a>
<form method="POST" style="margin: 0;" onsubmit="return confirm('Möchtest du diese News wirklich unwiderruflich löschen?');">
<input type="hidden" name="delete_id" value="<?= $news_item['id'] ?>">
@@ -389,11 +410,59 @@ $existing_news = $db->query("SELECT id, date, department, title FROM news ORDER
<?php endforeach; ?>
<?php if(empty($existing_news)): ?>
<tr>
<td colspan="4" style="text-align: center; padding: 20px; color: #888;">Noch keine News in der Datenbank vorhanden.</td>
<td colspan="4" style="text-align: center; padding: 20px; color: #888;">Noch keine News vorhanden.</td>
</tr>
<?php endif; ?>
</table>
</div>
</div>
<!-- Javascript für Crop und Social Media -->
<script>
// CROPPER LOGIK
let cropper;
document.getElementById('image_file_input').addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(event) {
document.getElementById('image_to_crop').src = event.target.result;
document.getElementById('cropper_container').style.display = 'block';
if (cropper) cropper.destroy();
cropper = new Cropper(document.getElementById('image_to_crop'), {
aspectRatio: 4 / 3,
viewMode: 1,
autoCropArea: 1
});
};
reader.readAsDataURL(file);
}
});
document.getElementById('newsForm').addEventListener('submit', function(e) {
if (cropper) {
const canvas = cropper.getCroppedCanvas({ width: 800, height: 600 });
if(canvas) {
document.getElementById('cropped_image_data').value = canvas.toDataURL('image/jpeg', 0.85);
}
}
});
// SOCIAL MEDIA EXPORT LOGIK
function copySocialMediaText(title, department) {
const cleanDept = department.replace(/\s+/g, '');
const text = `🚨 NEUE NEWS VON DER SG 07 🚨\n\n📢 ${title}\n\nLies jetzt den ganzen Bericht auf unserer Website!\n👉 sg07-stleon.de\n\n#SG07StLeon #Vereinsliebe #${cleanDept} #StLeonRot`;
const tempInput = document.createElement("textarea");
tempInput.value = text;
document.body.appendChild(tempInput);
tempInput.select();
document.execCommand("copy");
document.body.removeChild(tempInput);
alert("✅ Der Text für Instagram & Facebook wurde in deine Zwischenablage kopiert!\n\nDu kannst jetzt einfach rüber zu Insta gehen, dein Bild einfügen und beim Text auf 'Einsetzen' drücken.");
}
</script>
</body>
</html>

8
gitea_deploy_key Normal file
View File

@@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACA9K1g7EYM4GXW56/fwbXLZlWn7rP9AP1Gk8cC16WxTywAAAKjYMmgw2DJo
MAAAAAtzc2gtZWQyNTUxOQAAACA9K1g7EYM4GXW56/fwbXLZlWn7rP9AP1Gk8cC16WxTyw
AAAEA93szDRUXvB7RSxG1lUz9P/zKbzFIH2zQv1onw3QYyZT0rWDsRgzgZdbnr9/BtctmV
afus/0A/UaTxwLXpbFPLAAAAIGhlaWRlbGJlcmctaXRcaGVpbkBIRElULVBGNFBSR0hTAQ
IDBAU=
-----END OPENSSH PRIVATE KEY-----

1
gitea_deploy_key.pub Normal file
View File

@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID0rWDsRgzgZdbnr9/BtctmVafus/0A/UaTxwLXpbFPL heidelberg-it\hein@HDIT-PF4PRGHS