267 lines
12 KiB
PHP
267 lines
12 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
session_start();
|
|
}
|
|
|
|
require_once __DIR__ . '/../db/config.php';
|
|
require_once __DIR__ . '/content.php';
|
|
|
|
function h(?string $value): string
|
|
{
|
|
return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
}
|
|
|
|
function copy_text(array $copy, string $lang = 'id'): string
|
|
{
|
|
return $copy[$lang] ?? ($copy['id'] ?? '');
|
|
}
|
|
|
|
function copy_attrs(array $copy, bool $html = false): string
|
|
{
|
|
$extra = $html ? ' data-copy-html="1"' : '';
|
|
return ' data-copy-id="' . h($copy['id'] ?? '') . '" data-copy-jp="' . h($copy['jp'] ?? '') . '"' . $extra;
|
|
}
|
|
|
|
function placeholder_attrs(array $copy): string
|
|
{
|
|
return ' data-placeholder-id="' . h($copy['id'] ?? '') . '" data-placeholder-jp="' . h($copy['jp'] ?? '') . '"';
|
|
}
|
|
|
|
function asset_url(string $path): string
|
|
{
|
|
$full = __DIR__ . '/../' . ltrim($path, '/');
|
|
$version = is_file($full) ? (string) filemtime($full) : (string) time();
|
|
return $path . '?v=' . $version;
|
|
}
|
|
|
|
function project_name(): string
|
|
{
|
|
return $_SERVER['PROJECT_NAME'] ?? 'KOBA Entertainment Indonesia';
|
|
}
|
|
|
|
function page_title(string $title): string
|
|
{
|
|
return $title . ' | ' . project_name();
|
|
}
|
|
|
|
function render_head(string $title, string $description, array $options = []): void
|
|
{
|
|
$pageDescription = $description;
|
|
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? $pageDescription;
|
|
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|
$robots = $options['robots'] ?? 'index, follow';
|
|
echo '<!doctype html><html lang="id"><head>';
|
|
echo '<meta charset="utf-8" />';
|
|
echo '<meta name="viewport" content="width=device-width, initial-scale=1" />';
|
|
echo '<title>' . h(page_title($title)) . '</title>';
|
|
echo '<meta name="description" content="' . h($projectDescription ?: $pageDescription) . '" />';
|
|
echo '<meta name="robots" content="' . h($robots) . '" />';
|
|
echo '<meta name="theme-color" content="#0e2a66" />';
|
|
echo '<meta property="og:title" content="' . h(page_title($title)) . '" />';
|
|
echo '<meta property="og:description" content="' . h($projectDescription ?: $pageDescription) . '" />';
|
|
echo '<meta property="og:type" content="website" />';
|
|
echo '<meta property="twitter:card" content="summary_large_image" />';
|
|
echo '<meta property="twitter:title" content="' . h(page_title($title)) . '" />';
|
|
echo '<meta property="twitter:description" content="' . h($projectDescription ?: $pageDescription) . '" />';
|
|
if ($projectImageUrl) {
|
|
echo '<meta property="og:image" content="' . h($projectImageUrl) . '" />';
|
|
echo '<meta property="twitter:image" content="' . h($projectImageUrl) . '" />';
|
|
}
|
|
echo '<link rel="preconnect" href="https://fonts.googleapis.com">';
|
|
echo '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>';
|
|
echo '<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">';
|
|
echo '<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">';
|
|
echo '<link rel="stylesheet" href="' . h(asset_url('assets/css/custom.css')) . '">';
|
|
echo '</head><body>';
|
|
}
|
|
|
|
function render_header(string $active = 'home'): void
|
|
{
|
|
$navItems = kei_nav_items();
|
|
?>
|
|
<header class="site-header">
|
|
<nav class="navbar navbar-expand-lg navbar-light py-0">
|
|
<div class="container-fluid kei-container">
|
|
<a class="navbar-brand site-brand" href="index.php" aria-label="KOBA Entertainment Indonesia homepage">
|
|
<span class="brand-mark">KEI</span>
|
|
<span class="brand-copy">
|
|
<strong>KOBA Entertainment Indonesia</strong>
|
|
<small>Modern entertainment bridge</small>
|
|
</span>
|
|
</a>
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#keiNav" aria-controls="keiNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
<div class="collapse navbar-collapse justify-content-end" id="keiNav">
|
|
<ul class="navbar-nav align-items-lg-center gap-lg-1 mb-3 mb-lg-0">
|
|
<?php foreach ($navItems as $item): ?>
|
|
<li class="nav-item">
|
|
<a class="nav-link <?= $active === $item['key'] ? 'active' : '' ?>" href="<?= h($item['href']) ?>"<?= copy_attrs($item['label']) ?>><?= h(copy_text($item['label'])) ?></a>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<div class="d-flex align-items-center gap-2 ms-lg-3">
|
|
<button class="btn btn-ghost btn-sm" type="button" data-lang-toggle aria-label="Switch language">
|
|
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1Zm4.95 6h-2.16a11.8 11.8 0 0 0-.74-3.16A5.53 5.53 0 0 1 12.95 7ZM8 2.52c.6.82 1.25 2.34 1.55 4.48H6.45C6.75 4.86 7.4 3.34 8 2.52ZM5.95 3.84A11.8 11.8 0 0 0 5.21 7H3.05a5.53 5.53 0 0 1 2.9-3.16ZM3.05 9h2.16c.1 1.12.36 2.2.74 3.16A5.53 5.53 0 0 1 3.05 9Zm4.95 4.48c-.6-.82-1.25-2.34-1.55-4.48h3.1c-.3 2.14-.95 3.66-1.55 4.48Zm2.05-1.32c.38-.96.64-2.04.74-3.16h2.16a5.53 5.53 0 0 1-2.9 3.16Z"/></svg>
|
|
<span class="ms-2" data-lang-current>ID</span>
|
|
</button>
|
|
<a class="btn btn-brand btn-sm" href="register.php"<?= copy_attrs(['id' => 'Daftar Sekarang', 'jp' => '今すぐ応募']) ?>>Daftar Sekarang</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
<?php
|
|
}
|
|
|
|
function render_footer(): void
|
|
{
|
|
?>
|
|
<footer class="site-footer section-space-sm">
|
|
<div class="container-fluid kei-container">
|
|
<div class="row g-4 align-items-end">
|
|
<div class="col-lg-6">
|
|
<p class="eyebrow mb-3"<?= copy_attrs(['id' => 'KOBA Entertainment Indonesia', 'jp' => 'KOBA Entertainment Indonesia']) ?>>KOBA Entertainment Indonesia</p>
|
|
<h2 class="footer-title"<?= copy_attrs(['id' => 'Building global entertainment from Indonesia.', 'jp' => 'インドネシアから世界水準のエンターテインメントを築く。']) ?>>Building global entertainment from Indonesia.</h2>
|
|
<p class="footer-copy"<?= copy_attrs(['id' => 'Situs resmi untuk branding perusahaan, kolaborasi proyek, dan konversi talent baru yang siap tumbuh bersama KEI.', 'jp' => '本サイトは、企業ブランディング、プロジェクト連携、新しいタレント募集のための公式窓口です。']) ?>>Situs resmi untuk branding perusahaan, kolaborasi proyek, dan konversi talent baru yang siap tumbuh bersama KEI.</p>
|
|
</div>
|
|
<div class="col-lg-6">
|
|
<div class="footer-grid">
|
|
<div>
|
|
<span class="footer-label"<?= copy_attrs(['id' => 'Kontak', 'jp' => '連絡先']) ?>>Kontak</span>
|
|
<a href="mailto:hello@kobaentertainment.id">hello@kobaentertainment.id</a>
|
|
<span>Jakarta, Indonesia</span>
|
|
</div>
|
|
<div>
|
|
<span class="footer-label"<?= copy_attrs(['id' => 'Navigasi', 'jp' => 'ナビゲーション']) ?>>Navigasi</span>
|
|
<a href="profile.php">Profil</a>
|
|
<a href="news.php">Berita</a>
|
|
<a href="admin/login.php">Admin Login</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="footer-bottom">
|
|
<span>© <?= date('Y') ?> KOBA Entertainment Indonesia</span>
|
|
<a href="healthz.php">/healthz</a>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
<div class="toast-container position-fixed top-0 end-0 p-3">
|
|
<div id="keiToast" class="toast align-items-center text-bg-dark border-0" role="status" aria-live="polite" aria-atomic="true">
|
|
<div class="d-flex">
|
|
<div class="toast-body">Bahasa Indonesia aktif.</div>
|
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous" defer></script>
|
|
<script src="<?= h(asset_url('assets/js/main.js')) ?>" defer></script>
|
|
</body></html>
|
|
<?php
|
|
}
|
|
|
|
function flash_set(string $key, $value): void
|
|
{
|
|
$_SESSION['_flash'][$key] = $value;
|
|
}
|
|
|
|
function flash_get(string $key)
|
|
{
|
|
$value = $_SESSION['_flash'][$key] ?? null;
|
|
unset($_SESSION['_flash'][$key]);
|
|
return $value;
|
|
}
|
|
|
|
function ensure_applications_table(): void
|
|
{
|
|
$sql = "CREATE TABLE IF NOT EXISTS talent_applications (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
full_name VARCHAR(190) NOT NULL,
|
|
email VARCHAR(190) NOT NULL,
|
|
whatsapp VARCHAR(50) NOT NULL,
|
|
category VARCHAR(50) NOT NULL,
|
|
photo_url VARCHAR(255) DEFAULT NULL,
|
|
video_url VARCHAR(255) DEFAULT NULL,
|
|
consent_accuracy TINYINT(1) NOT NULL DEFAULT 0,
|
|
consent_audition TINYINT(1) NOT NULL DEFAULT 0,
|
|
consent_contract TINYINT(1) NOT NULL DEFAULT 0,
|
|
consent_data TINYINT(1) NOT NULL DEFAULT 0,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'review',
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
|
|
db()->exec($sql);
|
|
}
|
|
|
|
function normalize_whatsapp(string $value): string
|
|
{
|
|
$value = preg_replace('/[^0-9+]/', '', $value) ?? '';
|
|
return trim($value);
|
|
}
|
|
|
|
function handle_optional_upload(string $field, array $allowedMimeMap, int $maxBytes, string $subdir): ?string
|
|
{
|
|
if (empty($_FILES[$field]) || (int) $_FILES[$field]['error'] === UPLOAD_ERR_NO_FILE) {
|
|
return null;
|
|
}
|
|
|
|
$file = $_FILES[$field];
|
|
if ((int) $file['error'] !== UPLOAD_ERR_OK) {
|
|
throw new RuntimeException('Upload gagal untuk ' . $field . '.');
|
|
}
|
|
if ((int) $file['size'] > $maxBytes) {
|
|
throw new RuntimeException('Ukuran file ' . $field . ' terlalu besar.');
|
|
}
|
|
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
|
$mime = (string) $finfo->file($file['tmp_name']);
|
|
if (!isset($allowedMimeMap[$mime])) {
|
|
throw new RuntimeException('Format file ' . $field . ' tidak didukung.');
|
|
}
|
|
$targetDir = __DIR__ . '/../assets/uploads/' . $subdir;
|
|
if (!is_dir($targetDir) && !mkdir($targetDir, 0775, true) && !is_dir($targetDir)) {
|
|
throw new RuntimeException('Folder upload tidak dapat dibuat.');
|
|
}
|
|
$filename = $subdir . '-' . date('YmdHis') . '-' . bin2hex(random_bytes(4)) . '.' . $allowedMimeMap[$mime];
|
|
$target = $targetDir . '/' . $filename;
|
|
if (!move_uploaded_file($file['tmp_name'], $target)) {
|
|
throw new RuntimeException('Gagal menyimpan file ' . $field . '.');
|
|
}
|
|
return 'assets/uploads/' . $subdir . '/' . $filename;
|
|
}
|
|
|
|
function admin_credentials(): array
|
|
{
|
|
$user = getenv('KEI_ADMIN_USER') ?: (getenv('ADMIN_EMAIL') ?: 'admin@kei.local');
|
|
$pass = getenv('KEI_ADMIN_PASSWORD') ?: (getenv('ADMIN_PASSWORD') ?: 'KEIadmin2026!');
|
|
return [$user, $pass];
|
|
}
|
|
|
|
function using_default_admin_credentials(): bool
|
|
{
|
|
return !getenv('KEI_ADMIN_USER') && !getenv('ADMIN_EMAIL') && !getenv('KEI_ADMIN_PASSWORD') && !getenv('ADMIN_PASSWORD');
|
|
}
|
|
|
|
function is_admin(): bool
|
|
{
|
|
return !empty($_SESSION['kei_admin_logged_in']);
|
|
}
|
|
|
|
function require_admin(): void
|
|
{
|
|
if (!is_admin()) {
|
|
header('Location: login.php');
|
|
exit;
|
|
}
|
|
}
|
|
|
|
function status_badge_class(string $status): string
|
|
{
|
|
return match ($status) {
|
|
'accepted' => 'badge bg-success-subtle text-success-emphasis',
|
|
'rejected' => 'badge bg-danger-subtle text-danger-emphasis',
|
|
default => 'badge bg-secondary-subtle text-secondary-emphasis',
|
|
};
|
|
}
|