39414-vm/admin_ads.php
2026-04-15 07:14:44 +00:00

372 lines
20 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/queue_bootstrap.php';
qh_boot();
qh_admin_handle_request();
// Ensure the tables exist
$pdo = db();
function qh_ads_column_exists(PDO $pdo, string $column): bool
{
static $cache = [];
if (array_key_exists($column, $cache)) {
return $cache[$column];
}
$stmt = $pdo->prepare(
"SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'hospital_ads' AND COLUMN_NAME = ?"
);
$stmt->execute([DB_NAME, $column]);
return $cache[$column] = ((int) $stmt->fetchColumn() > 0);
}
$pdo->exec("CREATE TABLE IF NOT EXISTS hospital_ads (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NULL,
video_path VARCHAR(255) NOT NULL,
media_type VARCHAR(20) NOT NULL DEFAULT 'video',
media_path VARCHAR(255) NULL,
is_active TINYINT(1) DEFAULT 1,
sort_order INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
if (!qh_ads_column_exists($pdo, 'media_type')) {
$pdo->exec("ALTER TABLE hospital_ads ADD COLUMN media_type VARCHAR(20) NOT NULL DEFAULT 'video' AFTER video_path");
}
if (!qh_ads_column_exists($pdo, 'media_path')) {
$pdo->exec("ALTER TABLE hospital_ads ADD COLUMN media_path VARCHAR(255) NULL AFTER media_type");
}
$pdo->exec("UPDATE hospital_ads SET media_type = 'video' WHERE media_type IS NULL OR media_type = ''");
$pdo->exec("UPDATE hospital_ads SET media_path = video_path WHERE (media_path IS NULL OR media_path = '') AND video_path <> ''");
$pdo->exec("CREATE TABLE IF NOT EXISTS hospital_news (
id INT AUTO_INCREMENT PRIMARY KEY,
phrase VARCHAR(1000) NOT NULL,
is_active TINYINT(1) DEFAULT 1,
sort_order INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
// Handle Form Submissions
$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = (string) ($_POST['action'] ?? '');
$adId = (int) ($_POST['ad_id'] ?? $_POST['video_id'] ?? 0);
if ($action === 'add_media' && !empty($_FILES['media']['name'])) {
$mediaFile = $_FILES['media'];
$title = trim((string) ($_POST['title'] ?? ''));
$ext = strtolower(pathinfo($mediaFile['name'], PATHINFO_EXTENSION));
$typeMap = [
'mp4' => 'video',
'webm' => 'video',
'ogg' => 'video',
'jpg' => 'image',
'jpeg' => 'image',
'png' => 'image',
'webp' => 'image',
'gif' => 'image',
];
if (!isset($typeMap[$ext])) {
$message = qh_t(
"Invalid media format (.$ext). Allowed formats: JPG, JPEG, PNG, WEBP, GIF, MP4, WebM, OGG.",
"تنسيق وسائط غير صالح (.$ext). الصيغ المسموحة: JPG, JPEG, PNG, WEBP, GIF, MP4, WebM, OGG."
);
} elseif ($mediaFile['error'] !== UPLOAD_ERR_OK) {
$errorMap = [
UPLOAD_ERR_INI_SIZE => 'File exceeds max size in php.ini.',
UPLOAD_ERR_FORM_SIZE => 'File exceeds max size in HTML form.',
UPLOAD_ERR_PARTIAL => 'File was partially uploaded.',
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder.',
UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk.',
UPLOAD_ERR_EXTENSION => 'A PHP extension stopped the upload.',
];
$errMsg = $errorMap[$mediaFile['error']] ?? 'Unknown error';
$message = qh_t('Upload error: ' . $errMsg, 'خطأ في الرفع: ' . $errMsg);
} else {
$mediaType = $typeMap[$ext];
$uploadDir = __DIR__ . ($mediaType === 'video' ? '/assets/videos/uploads/' : '/assets/images/uploads/');
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$filename = uniqid($mediaType === 'video' ? 'vid_' : 'img_') . '.' . $ext;
$dest = $uploadDir . $filename;
if (move_uploaded_file($mediaFile['tmp_name'], $dest)) {
$path = ($mediaType === 'video' ? 'assets/videos/uploads/' : 'assets/images/uploads/') . $filename;
$legacyVideoPath = $mediaType === 'video' ? $path : '';
$stmt = $pdo->prepare(
"INSERT INTO hospital_ads (title, video_path, media_type, media_path, is_active) VALUES (?, ?, ?, ?, 1)"
);
$stmt->execute([$title, $legacyVideoPath, $mediaType, $path]);
$message = $mediaType === 'video'
? qh_t('Video uploaded successfully.', 'تم رفع الفيديو بنجاح.')
: qh_t('Image uploaded successfully.', 'تم رفع الصورة بنجاح.');
} else {
$message = qh_t('Failed to save uploaded file.', 'فشل في حفظ الملف المرفوع.');
}
}
} elseif ($action === 'delete_media' && $adId > 0) {
$stmt = $pdo->prepare(
"SELECT COALESCE(NULLIF(media_path, ''), NULLIF(video_path, '')) AS file_path FROM hospital_ads WHERE id = ?"
);
$stmt->execute([$adId]);
$media = $stmt->fetch();
if ($media) {
$filePath = (string) ($media['file_path'] ?? '');
if ($filePath !== '') {
$fullPath = __DIR__ . '/' . $filePath;
if (file_exists($fullPath)) {
unlink($fullPath);
}
}
$pdo->prepare("DELETE FROM hospital_ads WHERE id = ?")->execute([$adId]);
$message = qh_t('Ad deleted successfully.', 'تم حذف الإعلان بنجاح.');
}
} elseif ($action === 'toggle_status' && $adId > 0) {
$stmt = $pdo->prepare("UPDATE hospital_ads SET is_active = NOT is_active WHERE id = ?");
$stmt->execute([$adId]);
$message = qh_t('Ad status updated.', 'تم تحديث حالة الإعلان.');
} elseif ($action === 'add_news' && !empty($_POST['phrase'])) {
$phrase = trim($_POST['phrase']);
$stmt = $pdo->prepare("INSERT INTO hospital_news (phrase, is_active) VALUES (?, 1)");
$stmt->execute([$phrase]);
$message = qh_t('Phrase added successfully.', 'تمت إضافة العبارة بنجاح.');
} elseif ($action === 'delete_news' && !empty($_POST['news_id'])) {
$id = (int) $_POST['news_id'];
$pdo->prepare("DELETE FROM hospital_news WHERE id = ?")->execute([$id]);
$message = qh_t('Phrase deleted successfully.', 'تم حذف العبارة بنجاح.');
} elseif ($action === 'toggle_news_status' && !empty($_POST['news_id'])) {
$id = (int) $_POST['news_id'];
$pdo->prepare("UPDATE hospital_news SET is_active = NOT is_active WHERE id = ?")->execute([$id]);
$message = qh_t('Phrase status updated.', 'تم تحديث حالة العبارة.');
}
}
// Fetch existing ads
$stmt = $pdo->query(
"SELECT *,
COALESCE(NULLIF(media_type, ''), 'video') AS effective_media_type,
COALESCE(NULLIF(media_path, ''), NULLIF(video_path, '')) AS effective_media_path
FROM hospital_ads
ORDER BY sort_order ASC, id DESC"
);
$ads = $stmt->fetchAll();
$stmt2 = $pdo->query("SELECT * FROM hospital_news ORDER BY sort_order ASC, id DESC");
$newsPhrases = $stmt2->fetchAll();
qh_page_start(
'admin_ads',
qh_t('Manage Advertisements', 'إدارة الإعلانات'),
qh_t('Upload and manage image and video ads for the queue display.', 'رفع وإدارة إعلانات الصور والفيديوهات لعرضها على شاشة الطابور.')
);
?>
<div class="container-fluid container-xxl px-3 px-lg-4">
<div class="admin-layout">
<aside class="admin-sidebar-column">
<?php qh_render_admin_sidebar('admin_ads.php'); ?>
</aside>
<div class="admin-content-stack">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Advertisements (Images & Videos)', 'الإعلانات (صور وفيديوهات)')) ?></h1>
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('Videos play until they end, while images stay on screen for 10 seconds in the same rotation.', 'تعمل الفيديوهات حتى نهايتها، بينما تبقى الصور على الشاشة لمدة 10 ثوانٍ ضمن نفس التدوير.')) ?></p>
</div>
</div>
<?php if ($message): ?>
<div class="alert alert-info shadow-sm"><?= qh_h($message) ?></div>
<?php endif; ?>
<div class="card shadow-sm border-0 mb-4">
<div class="card-header bg-white border-bottom py-3">
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Upload New Ad Media', 'رفع وسائط إعلان جديدة')) ?></h5>
</div>
<div class="card-body">
<form method="post" enctype="multipart/form-data" class="row g-3">
<input type="hidden" name="action" value="add_media">
<div class="col-md-6">
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Ad Title (Optional)', 'عنوان الإعلان (اختياري)')) ?></label>
<input type="text" name="title" class="form-control" placeholder="<?= qh_h(qh_t('e.g. Summer Promo', 'مثال: عرض الصيف')) ?>">
</div>
<div class="col-md-6">
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Image or Video File', 'ملف صورة أو فيديو')) ?></label>
<input type="file" name="media" class="form-control" accept="image/jpeg,image/png,image/webp,image/gif,video/mp4,video/webm,video/ogg" required>
<div class="form-text"><?= qh_h(qh_t('Formats: JPG, JPEG, PNG, WEBP, GIF, MP4, WebM, OGG.', 'الصيغ: JPG, JPEG, PNG, WEBP, GIF, MP4, WebM, OGG.')) ?></div>
</div>
<div class="col-12 mt-3">
<button type="submit" class="btn btn-primary px-4"><?= qh_h(qh_t('Upload Ad', 'رفع الإعلان')) ?></button>
</div>
</form>
</div>
</div>
<div class="card shadow-sm border-0">
<div class="card-header bg-white border-bottom py-3">
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Uploaded Ads', 'الإعلانات المرفوعة')) ?></h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th class="border-0 px-4 py-3"><?= qh_h(qh_t('Preview', 'معاينة')) ?></th>
<th class="border-0 py-3"><?= qh_h(qh_t('Title', 'العنوان')) ?></th>
<th class="border-0 py-3 text-center"><?= qh_h(qh_t('Type', 'النوع')) ?></th>
<th class="border-0 py-3 text-center"><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
<th class="border-0 px-4 py-3 text-end"><?= qh_h(qh_t('Actions', 'الإجراءات')) ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($ads)): ?>
<tr>
<td colspan="5" class="text-center py-4 text-muted"><?= qh_h(qh_t('No ads uploaded yet.', 'لم يتم رفع أي إعلانات بعد.')) ?></td>
</tr>
<?php else: ?>
<?php foreach ($ads as $ad): ?>
<tr>
<td class="px-4 py-3">
<?php if (($ad['effective_media_type'] ?? 'video') === 'image'): ?>
<img src="<?= qh_h((string) $ad['effective_media_path']) ?>" alt="<?= qh_h($ad['title'] ?: qh_t('Advertisement preview', 'معاينة الإعلان')) ?>" width="120" height="68" class="rounded shadow-sm border object-fit-cover">
<?php else: ?>
<video width="120" class="rounded shadow-sm border" muted playsinline>
<source src="<?= qh_h((string) $ad['effective_media_path']) ?>" type="video/mp4">
</video>
<?php endif; ?>
</td>
<td class="py-3 fw-semibold text-dark">
<?= qh_h($ad['title'] ?: qh_t('Untitled', 'بدون عنوان')) ?>
</td>
<td class="py-3 text-center">
<span class="badge <?= ($ad['effective_media_type'] ?? 'video') === 'image' ? 'bg-info-subtle text-info-emphasis' : 'bg-dark-subtle text-dark-emphasis' ?> rounded-pill px-3">
<?= qh_h(($ad['effective_media_type'] ?? 'video') === 'image' ? qh_t('Image', 'صورة') : qh_t('Video', 'فيديو')) ?>
</span>
</td>
<td class="py-3 text-center">
<?php if ($ad['is_active']): ?>
<span class="badge bg-success rounded-pill px-3"><?= qh_h(qh_t('Active', 'مفعل')) ?></span>
<?php else: ?>
<span class="badge bg-secondary rounded-pill px-3"><?= qh_h(qh_t('Inactive', 'غير مفعل')) ?></span>
<?php endif; ?>
</td>
<td class="px-4 py-3 text-end">
<div class="d-flex justify-content-end gap-2">
<form method="post" class="m-0 p-0">
<input type="hidden" name="action" value="toggle_status">
<input type="hidden" name="ad_id" value="<?= (int) $ad['id'] ?>">
<button type="submit" class="btn btn-sm <?= $ad['is_active'] ? 'btn-outline-secondary' : 'btn-outline-success' ?>">
<?= qh_h($ad['is_active'] ? qh_t('Disable', 'تعطيل') : qh_t('Enable', 'تفعيل')) ?>
</button>
</form>
<form method="post" class="m-0 p-0" onsubmit="return confirm('<?= qh_h(qh_t('Are you sure you want to delete this ad?', 'هل أنت متأكد أنك تريد حذف هذا الإعلان؟')) ?>');">
<input type="hidden" name="action" value="delete_media">
<input type="hidden" name="ad_id" value="<?= (int) $ad['id'] ?>">
<button type="submit" class="btn btn-sm btn-outline-danger"><?= qh_h(qh_t('Delete', 'حذف')) ?></button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- News Ticker Section -->
<div class="d-flex justify-content-between align-items-center mb-4 mt-5">
<div>
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('News Ticker Phrases', 'عبارات شريط الأخبار')) ?></h1>
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('These phrases will scroll at the bottom of the display screen.', 'ستتحرك هذه العبارات في أسفل شاشة العرض.')) ?></p>
</div>
</div>
<div class="card shadow-sm border-0 mb-4">
<div class="card-header bg-white border-bottom py-3">
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Add New Phrase', 'إضافة عبارة جديدة')) ?></h5>
</div>
<div class="card-body">
<form method="post" class="row g-3">
<input type="hidden" name="action" value="add_news">
<div class="col-md-10">
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Phrase Text', 'نص العبارة')) ?></label>
<input type="text" name="phrase" class="form-control" placeholder="<?= qh_h(qh_t('e.g. Welcome to our hospital. Please wait for your turn.', 'مثال: أهلاً بكم في مستشفانا. يرجى انتظار دوركم.')) ?>" required>
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100"><?= qh_h(qh_t('Add', 'إضافة')) ?></button>
</div>
</form>
</div>
</div>
<div class="card shadow-sm border-0 mb-4">
<div class="card-header bg-white border-bottom py-3">
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Ticker Phrases', 'عبارات الشريط')) ?></h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th class="border-0 px-4 py-3"><?= qh_h(qh_t('Phrase', 'العبارة')) ?></th>
<th class="border-0 py-3 text-center" style="width:120px;"><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
<th class="border-0 px-4 py-3 text-end" style="width:200px;"><?= qh_h(qh_t('Actions', 'الإجراءات')) ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($newsPhrases)): ?>
<tr>
<td colspan="3" class="text-center py-4 text-muted"><?= qh_h(qh_t('No phrases added yet.', 'لم يتم إضافة عبارات بعد.')) ?></td>
</tr>
<?php else: ?>
<?php foreach ($newsPhrases as $n): ?>
<tr>
<td class="px-4 py-3 fw-semibold text-dark">
<?= qh_h($n['phrase']) ?>
</td>
<td class="py-3 text-center">
<?php if ($n['is_active']): ?>
<span class="badge bg-success rounded-pill px-3"><?= qh_h(qh_t('Active', 'مفعل')) ?></span>
<?php else: ?>
<span class="badge bg-secondary rounded-pill px-3"><?= qh_h(qh_t('Inactive', 'غير مفعل')) ?></span>
<?php endif; ?>
</td>
<td class="px-4 py-3 text-end">
<div class="d-flex justify-content-end gap-2">
<form method="post" class="m-0 p-0">
<input type="hidden" name="action" value="toggle_news_status">
<input type="hidden" name="news_id" value="<?= (int) $n['id'] ?>">
<button type="submit" class="btn btn-sm <?= $n['is_active'] ? 'btn-outline-secondary' : 'btn-outline-success' ?>">
<?= qh_h($n['is_active'] ? qh_t('Disable', 'تعطيل') : qh_t('Enable', 'تفعيل')) ?>
</button>
</form>
<form method="post" class="m-0 p-0" onsubmit="return confirm('<?= qh_h(qh_t('Are you sure you want to delete this phrase?', 'هل أنت متأكد أنك تريد حذف هذه العبارة؟')) ?>');">
<input type="hidden" name="action" value="delete_news">
<input type="hidden" name="news_id" value="<?= (int) $n['id'] ?>">
<button type="submit" class="btn btn-sm btn-outline-danger"><?= qh_h(qh_t('Delete', 'حذف')) ?></button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<?php qh_page_end(); ?>