322 lines
15 KiB
PHP
322 lines
15 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../../public/includes/init_admin.php';
|
|
|
|
/** УКАЖИ ID администратора-практиканта здесь */
|
|
const ADMIN_PRACTICIEN_ID = 88888888; // <-- заменить на реальный ID
|
|
$me = (int)($_SESSION['reference_id'] ?? 0);
|
|
if ($me !== ADMIN_PRACTICIEN_ID) {
|
|
http_response_code(403);
|
|
echo '<div class="container py-5"><div class="alert alert-danger">'
|
|
. htmlspecialchars($translations['non_autorise'] ?? 'Доступ запрещён.', ENT_QUOTES, 'UTF-8')
|
|
. '</div></div>';
|
|
return;
|
|
}
|
|
|
|
$mustReauth = function(int $ttl=600){
|
|
$t=(int)($_SESSION['admin_reauth_at']??0);
|
|
return ($t===0||(time()-$t)>$ttl);
|
|
};
|
|
if ($mustReauth()){
|
|
$qs=http_build_query(['page'=>'reauth','redir'=>($_SERVER['REQUEST_URI']??'index.php?page=maintenance')]);
|
|
header('Location:?'.$qs);
|
|
exit;
|
|
}
|
|
|
|
// -- PDO si absent
|
|
if (!isset($pdo)) { require_once '../../includes/db.php'; }
|
|
|
|
// CSRF
|
|
if (empty($_SESSION['csrf_token'])) {
|
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
|
}
|
|
$CSRF = $_SESSION['csrf_token'];
|
|
|
|
// helpers
|
|
function e($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
|
|
|
$flash = ['type'=>null,'msg'=>null];
|
|
|
|
// ---- Обработка POST ----
|
|
$action = $_POST['do'] ?? null;
|
|
|
|
try {
|
|
if ($action === 'update_main') {
|
|
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf'] ?? '')) throw new Exception('CSRF');
|
|
$pid = (int)($_POST['id'] ?? 0);
|
|
$url = trim($_POST['video'] ?? '');
|
|
$stmt = $pdo->prepare("UPDATE praticien SET video = :video WHERE id = :id");
|
|
$stmt->execute(['video' => ($url !== '' ? $url : null), 'id' => $pid]);
|
|
$flash = ['type'=>'success','msg'=>($translations['admin_video_flash_main_saved'] ?? 'Основное видео сохранено.')];
|
|
$_GET['id'] = $pid;
|
|
}
|
|
|
|
if ($action === 'add_extra') {
|
|
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf'] ?? '')) throw new Exception('CSRF');
|
|
$pid = (int)($_POST['id'] ?? 0);
|
|
$url = trim($_POST['url'] ?? '');
|
|
$title = trim($_POST['title'] ?? '');
|
|
$ordre = (int)($_POST['ordre'] ?? 0);
|
|
if ($url === '') throw new Exception($translations['admin_video_err_url_required'] ?? 'Укажите URL');
|
|
|
|
$stmt = $pdo->prepare("INSERT INTO praticien_video (praticien_id, url, title, ordre) VALUES (?,?,?,?)");
|
|
$stmt->execute([$pid, $url, ($title !== '' ? $title : null), $ordre]);
|
|
$flash = ['type'=>'success','msg'=>($translations['admin_video_flash_added'] ?? 'Видео добавлено.')];
|
|
$_GET['id'] = $pid;
|
|
}
|
|
|
|
if ($action === 'update_extra') {
|
|
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf'] ?? '')) throw new Exception('CSRF');
|
|
$pid = (int)($_POST['pid'] ?? 0);
|
|
$vid = (int)($_POST['vid'] ?? 0);
|
|
$url = trim($_POST['url'] ?? '');
|
|
$title = trim($_POST['title'] ?? '');
|
|
$ordre = (int)($_POST['ordre'] ?? 0);
|
|
if ($vid <= 0 || $url === '') throw new Exception($translations['admin_video_err_check_fields'] ?? 'Проверьте поля');
|
|
|
|
$stmt = $pdo->prepare("UPDATE praticien_video SET url=?, title=?, ordre=? WHERE id=?");
|
|
$stmt->execute([$url, ($title !== '' ? $title : null), $ordre, $vid]);
|
|
$flash = ['type'=>'success','msg'=>($translations['admin_video_flash_updated'] ?? 'Видео обновлено.')];
|
|
$_GET['id'] = $pid;
|
|
}
|
|
|
|
if ($action === 'delete_extra') {
|
|
if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf'] ?? '')) throw new Exception('CSRF');
|
|
$pid = (int)($_POST['pid'] ?? 0);
|
|
$vid = (int)($_POST['vid'] ?? 0);
|
|
if ($vid <= 0) throw new Exception($translations['admin_video_err_unknown_id'] ?? 'Неизвестный ID');
|
|
|
|
$stmt = $pdo->prepare("DELETE FROM praticien_video WHERE id=?");
|
|
$stmt->execute([$vid]);
|
|
$flash = ['type'=>'success','msg'=>($translations['admin_video_flash_deleted'] ?? 'Видео удалено.')];
|
|
$_GET['id'] = $pid;
|
|
}
|
|
} catch (Exception $ex) {
|
|
$flash = ['type'=>'danger','msg'=>($translations['err_prefixe'] ?? 'Ошибка: ') . $ex->getMessage()];
|
|
}
|
|
|
|
// ---- РЕЖИМ A: список всех практиков ----
|
|
if (empty($_GET['id'])) {
|
|
$rows = $pdo->query("
|
|
SELECT id, titre, nom, prenom, specialite, photo, video
|
|
FROM praticien
|
|
ORDER BY nom ASC, prenom ASC
|
|
")->fetchAll(PDO::FETCH_ASSOC);
|
|
?>
|
|
|
|
<div class="shadow p-4 rounded bg-light">
|
|
<h2 class="mb-3 text-center"><?= e($translations['admin_video_title'] ?? '🎬 Видео практиков — Администрирование') ?></h2>
|
|
|
|
<?php if ($flash['msg']): ?>
|
|
<div class="alert alert-<?= e($flash['type']) ?>"><?= e($flash['msg']) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="mb-3">
|
|
<input type="text" class="form-control" id="filter"
|
|
placeholder="<?= e($translations['admin_video_filter_placeholder'] ?? 'Фильтр (имя, специальность)…') ?>">
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-hover align-middle mobile-cards" id="tab">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="width:70px;"><?= e($translations['admin_video_th_photo'] ?? 'Фото') ?></th>
|
|
<th><?= e($translations['admin_video_th_name'] ?? 'Имя') ?></th>
|
|
<th><?= e($translations['admin_video_th_specialite'] ?? 'Специальность') ?></th>
|
|
<th><?= e($translations['admin_video_th_main_video'] ?? 'Основное видео') ?></th>
|
|
<th class="text-center" style="width:150px;"><?= e($translations['admin_video_th_actions'] ?? 'Действия') ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($rows as $r):
|
|
$name = trim($r['prenom'].' '.$r['nom']);
|
|
$photo = !empty($r['photo']) ? '/public/uploads/'.e($r['photo']) : '/public/uploads/img/user_placeholder.png';
|
|
?>
|
|
<tr data-hay="<?= e(mb_strtolower($name.' '.$r['specialite'])) ?>">
|
|
<td><img src="<?= $photo ?>" alt="" class="rounded" style="width:48px;height:48px;object-fit:cover;"></td>
|
|
<td><?= e($name) ?></td>
|
|
<td><?= e($r['specialite'] ?? '') ?></td>
|
|
<td>
|
|
<?php if (!empty($r['video'])): ?>
|
|
<span class="badge bg-success"><?= e($translations['admin_video_badge_set'] ?? 'указано') ?></span>
|
|
<a class="ms-2" href="<?= e($r['video']) ?>" target="_blank" rel="noopener">
|
|
<?= e($translations['admin_video_open'] ?? 'открыть') ?>
|
|
</a>
|
|
<?php else: ?>
|
|
<span class="badge bg-secondary"><?= e($translations['admin_video_badge_not_set'] ?? 'не указано') ?></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td class="text-center">
|
|
<a class="btn btn-sm btn-danger" href="index.php?page=add_video&id=<?= (int)$r['id'] ?>">
|
|
<?= e($translations['admin_video_manage'] ?? 'Управлять') ?>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Простой фильтр по таблице (на клиенте)
|
|
document.getElementById('filter').addEventListener('input', function(){
|
|
const q = (this.value || '').trim().toLowerCase();
|
|
document.querySelectorAll('#tab tbody tr').forEach(tr=>{
|
|
const hay = (tr.dataset.hay || '').toLowerCase();
|
|
tr.style.display = !q || hay.includes(q) ? '' : 'none';
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<?php
|
|
return;
|
|
}
|
|
|
|
// ---- РЕЖИМ B: управление конкретным практиком ----
|
|
$pid = (int)$_GET['id'];
|
|
|
|
$st = $pdo->prepare("SELECT id, titre, nom, prenom, specialite, photo, video FROM praticien WHERE id=?");
|
|
$st->execute([$pid]);
|
|
$praticien = $st->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$praticien) {
|
|
echo '<div class="container py-5"><div class="alert alert-warning">'
|
|
. e($translations['admin_video_not_found'] ?? 'Практикант не найден.')
|
|
. '</div><a href="add_video.php" class="btn btn-secondary mt-2">⬅️ '
|
|
. e($translations['admin_video_back'] ?? 'Назад')
|
|
. '</a></div>';
|
|
return;
|
|
}
|
|
|
|
$extras = $pdo->prepare("SELECT id, url, title, ordre, created_at FROM praticien_video WHERE praticien_id=? ORDER BY ordre ASC, id DESC");
|
|
$extras->execute([$pid]);
|
|
$videos = $extras->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$name = trim($praticien['prenom'].' '.$praticien['nom']);
|
|
$photo = !empty($praticien['photo']) ? '/public/uploads/'.e($praticien['photo']) : '/public/uploads/img/user_placeholder.png';
|
|
?>
|
|
|
|
<div class="shadow p-4 rounded bg-light">
|
|
|
|
<a href="index.php?page=add_video" class="btn btn-outline-secondary mb-3" style="border-radius:999px;">
|
|
⬅️ <?= e($translations['admin_video_back_to_list'] ?? 'Назад к списку') ?>
|
|
</a>
|
|
|
|
<?php if ($flash['msg']): ?>
|
|
<div class="alert alert-<?= e($flash['type']) ?>"><?= e($flash['msg']) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="card shadow-sm mb-4">
|
|
<div class="card-body d-flex gap-3 align-items-center">
|
|
<img src="<?= $photo ?>" class="rounded" style="width:64px;height:64px;object-fit:cover;" alt="">
|
|
<div>
|
|
<h5 class="mb-1"><?= e($name) ?></h5>
|
|
<div class="text-muted"><?= e($praticien['specialite'] ?? '') ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Основное видео (praticien.video) -->
|
|
<div class="card shadow-sm mb-4">
|
|
<div class="card-header bg-light"><strong><?= e($translations['admin_video_main_block_title'] ?? 'Основное видео') ?></strong></div>
|
|
<div class="card-body">
|
|
<form method="post" class="row g-3">
|
|
<div class="col-12">
|
|
<label class="form-label"><?= e($translations['admin_video_main_url_label'] ?? 'URL видео (YouTube, Vimeo, Dailymotion, MP4…)') ?></label>
|
|
<input type="url" name="video" class="form-control"
|
|
placeholder="<?= e($translations['admin_video_url_placeholder'] ?? 'https://…') ?>"
|
|
value="<?= e($praticien['video'] ?? '') ?>">
|
|
<div class="form-text"><?= e($translations['admin_video_main_help'] ?? 'Поле сохраняется в praticien.video и используется на публичных страницах.') ?></div>
|
|
</div>
|
|
<div class="col-12">
|
|
<input type="hidden" name="csrf" value="<?= e($CSRF) ?>">
|
|
<input type="hidden" name="id" value="<?= (int)$pid ?>">
|
|
<input type="hidden" name="do" value="update_main">
|
|
<button class="btn btn-danger" style="border-radius:999px;"><?= e($translations['enregistrer'] ?? 'Сохранить') ?></button>
|
|
<?php if (!empty($praticien['video'])): ?>
|
|
<a class="btn btn-outline-secondary ms-2" target="_blank" rel="noopener" href="<?= e($praticien['video']) ?>">
|
|
<?= e($translations['admin_video_open_video'] ?? 'Открыть видео') ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Дополнительные видео (praticien_video) -->
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-light"><strong><?= e($translations['admin_video_extra_block_title'] ?? 'Дополнительные видео (необязательно)') ?></strong></div>
|
|
<div class="card-body">
|
|
<form method="post" class="row g-3 mb-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label"><?= e($translations['admin_video_title_label_optional'] ?? 'Название (необязательно)') ?></label>
|
|
<input type="text" name="title" class="form-control"
|
|
placeholder="<?= e($translations['admin_video_title_placeholder'] ?? 'Презентация кабинета') ?>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label"><?= e($translations['admin_video_order_label'] ?? 'Порядок') ?></label>
|
|
<input type="number" name="ordre" class="form-control" value="0" min="0" step="1">
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label"><?= e($translations['admin_video_url_label'] ?? 'URL видео') ?></label>
|
|
<input type="url" name="url" class="form-control"
|
|
placeholder="<?= e($translations['admin_video_url_placeholder'] ?? 'https://…') ?>">
|
|
</div>
|
|
<div class="col-12">
|
|
<input type="hidden" name="csrf" value="<?= e($CSRF) ?>">
|
|
<input type="hidden" name="id" value="<?= (int)$pid ?>">
|
|
<input type="hidden" name="do" value="add_extra">
|
|
<button class="btn btn-success" style="border-radius:999px;"><?= e($translations['admin_video_add'] ?? 'Добавить') ?></button>
|
|
</div>
|
|
</form>
|
|
|
|
<?php if (empty($videos)): ?>
|
|
<div class="text-muted"><?= e($translations['admin_video_no_extras'] ?? 'Пока нет дополнительных видео.') ?></div>
|
|
<?php else: ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="width:80px;"><?= e($translations['admin_video_th_order'] ?? 'Порядок') ?></th>
|
|
<th><?= e($translations['admin_video_th_title'] ?? 'Название') ?></th>
|
|
<th><?= e($translations['admin_video_th_url'] ?? 'URL') ?></th>
|
|
<th style="width:180px;"><?= e($translations['admin_video_th_actions'] ?? 'Действия') ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($videos as $v): ?>
|
|
<tr>
|
|
<form method="post">
|
|
<td style="width:80px;">
|
|
<input type="number" name="ordre" class="form-control" value="<?= (int)$v['ordre'] ?>" min="0" step="1">
|
|
</td>
|
|
<td>
|
|
<input type="text" name="title" class="form-control" value="<?= e($v['title']) ?>"
|
|
placeholder="<?= e($translations['admin_video_title_optional_placeholder'] ?? 'Название (необязательно)') ?>">
|
|
</td>
|
|
<td>
|
|
<input type="url" name="url" class="form-control" value="<?= e($v['url']) ?>">
|
|
</td>
|
|
<td class="d-flex gap-2">
|
|
<input type="hidden" name="csrf" value="<?= e($CSRF) ?>">
|
|
<input type="hidden" name="pid" value="<?= (int)$pid ?>">
|
|
<input type="hidden" name="vid" value="<?= (int)$v['id'] ?>">
|
|
<button name="do" value="update_extra" class="btn btn-outline-primary btn-sm">
|
|
<?= e($translations['enregistrer'] ?? 'Сохранить') ?>
|
|
</button>
|
|
<button name="do" value="delete_extra" class="btn btn-outline-danger btn-sm"
|
|
onclick="return confirm('<?= e($translations['admin_video_confirm_delete'] ?? 'Удалить это видео?') ?>')">
|
|
<?= e($translations['supprimer'] ?? 'Удалить') ?>
|
|
</button>
|
|
</td>
|
|
</form>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|