-
-
-
-
-
-
憛怠神?
-
???蝣箄?銝銝?/h3>
-
- - ?? 瑼??澆?嚗蔣?????嚗遣霅啣瑼?100MB 隞亙
- - ?? ?遢鞈???瘞湔?梯?蝷曉?冽??30TH ?隡?雿輻
- - ?? ?敺蝣箏???嚗?敺??西??砍?蝺渡???鋆賣???/li>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/yellow-note/dance-application-submit.php b/yellow-note/dance-application-submit.php
deleted file mode 100644
index 5bfffd5..0000000
--- a/yellow-note/dance-application-submit.php
+++ /dev/null
@@ -1,170 +0,0 @@
- $maxUploadBytes + 1024 * 1024) {
- return_to_dance_form('file_too_large');
-}
-
-// 隱藏欄位有內容時視為機器送件;不透露攔截規則。
-if (trim((string) ($_POST['website'] ?? '')) !== '') {
- return_to_dance_form('success');
-}
-
-$nickname = post_value('nickname', 80);
-$email = mb_strtolower(post_value('email', 190), 'UTF-8');
-$danceYearsRaw = trim((string) ($_POST['dance_years'] ?? ''));
-$availableDate = trim((string) ($_POST['available_date'] ?? ''));
-$attended20th = trim((string) ($_POST['attended_20th'] ?? ''));
-$song = trim((string) ($_POST['song'] ?? ''));
-$message30th = post_value('message_30th', 5000);
-
-$allowedAttendance = ['attended', 'watched_video', 'first_time'];
-$allowedContent = ['cover', 'medley', 'blessing', 'graphic', 'editing', 'archive'];
-$allowedSongs = ['', 'couple', 'comeback', 'road_fighter', 'pom_saeng_pom_sa', 'other'];
-
-$selectedContent = $_POST['participate_content'] ?? [];
-if (!is_array($selectedContent)) {
- $selectedContent = [$selectedContent];
-}
-$selectedContent = array_values(array_unique(array_intersect(
- $allowedContent,
- array_map(static fn ($value): string => trim((string) $value), $selectedContent)
-)));
-
-if ($nickname === '' || !filter_var($email, FILTER_VALIDATE_EMAIL) || !$selectedContent) {
- return_to_dance_form('invalid');
-}
-if ($attended20th !== '' && !in_array($attended20th, $allowedAttendance, true)) {
- return_to_dance_form('invalid');
-}
-if (!in_array($song, $allowedSongs, true)) {
- return_to_dance_form('invalid');
-}
-
-$danceYears = null;
-if ($danceYearsRaw !== '') {
- $danceYearsFilter = filter_var($danceYearsRaw, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 30]]);
- if ($danceYearsFilter === false) {
- return_to_dance_form('invalid');
- }
- $danceYears = (int) $danceYearsFilter;
-}
-
-if ($availableDate !== '') {
- $date = DateTimeImmutable::createFromFormat('!Y-m-d', $availableDate);
- if (!$date || $date->format('Y-m-d') !== $availableDate) {
- return_to_dance_form('invalid');
- }
-}
-
-$db = $config['db'];
-$dsn = "mysql:host={$db['host']};port={$db['port']};dbname={$db['name']};charset=utf8mb4";
-try {
- $pdo = new PDO($dsn, $db['user'], $db['pass'], [
- PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
- PDO::ATTR_EMULATE_PREPARES => false,
- ]);
-} catch (PDOException) {
- return_to_dance_form('unavailable');
-}
-
-$submittedIp = mb_substr((string) ($_SERVER['REMOTE_ADDR'] ?? ''), 0, 45, 'UTF-8');
-if ($submittedIp !== '') {
- $rate = $pdo->prepare('SELECT COUNT(*) FROM dance_applications WHERE submitted_ip = ? AND created_at >= DATE_SUB(NOW(), INTERVAL 2 MINUTE)');
- $rate->execute([$submittedIp]);
- if ((int) $rate->fetchColumn() >= 3) {
- return_to_dance_form('too_many');
- }
-}
-
-$storedFileName = null;
-$originalFileName = null;
-$mimeType = null;
-$fileSize = null;
-$file = $_FILES['reference_file'] ?? null;
-if (is_array($file) && (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE) {
- if ((int) $file['error'] !== UPLOAD_ERR_OK || (int) $file['size'] > $maxUploadBytes) {
- return_to_dance_form('file_too_large');
- }
-
- $allowedMimeTypes = [
- 'image/jpeg' => 'jpg',
- 'image/png' => 'png',
- 'image/gif' => 'gif',
- 'image/webp' => 'webp',
- 'video/mp4' => 'mp4',
- 'video/webm' => 'webm',
- 'video/quicktime' => 'mov',
- ];
- $mimeType = (new finfo(FILEINFO_MIME_TYPE))->file((string) $file['tmp_name']) ?: '';
- if (!isset($allowedMimeTypes[$mimeType])) {
- return_to_dance_form('file_type');
- }
-
- $uploadDir = (string) $config['dance_upload_dir'];
- if (!is_dir($uploadDir) && !mkdir($uploadDir, 0750, true) && !is_dir($uploadDir)) {
- return_to_dance_form('unavailable');
- }
- $storedFileName = bin2hex(random_bytes(20)) . '.' . $allowedMimeTypes[$mimeType];
- if (!move_uploaded_file((string) $file['tmp_name'], $uploadDir . DIRECTORY_SEPARATOR . $storedFileName)) {
- return_to_dance_form('unavailable');
- }
- $originalFileName = mb_substr(basename((string) $file['name']), 0, 255, 'UTF-8');
- $fileSize = (int) $file['size'];
-}
-
-try {
- $stmt = $pdo->prepare(
- 'INSERT INTO dance_applications
- (nickname, email, dance_years, available_date, attended_20th, participate_content, song,
- reference_file_name, reference_original_name, reference_mime_type, reference_file_size,
- message_30th, submitted_ip, user_agent)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
- );
- $stmt->execute([
- $nickname,
- $email,
- $danceYears,
- $availableDate !== '' ? $availableDate : null,
- $attended20th !== '' ? $attended20th : null,
- implode(',', $selectedContent),
- $song !== '' ? $song : null,
- $storedFileName,
- $originalFileName,
- $mimeType,
- $fileSize,
- $message30th !== '' ? $message30th : null,
- $submittedIp !== '' ? $submittedIp : null,
- mb_substr((string) ($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255, 'UTF-8'),
- ]);
-} catch (PDOException) {
- if ($storedFileName !== null) {
- @unlink((string) $config['dance_upload_dir'] . DIRECTORY_SEPARATOR . $storedFileName);
- }
- return_to_dance_form('unavailable');
-}
-
-return_to_dance_form('success');
diff --git a/yellow-note/dance-practices-data.php b/yellow-note/dance-practices-data.php
deleted file mode 100644
index 8deaf85..0000000
--- a/yellow-note/dance-practices-data.php
+++ /dev/null
@@ -1,52 +0,0 @@
- PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
- PDO::ATTR_EMULATE_PREPARES => false,
- ]);
- $rows = $pdo->query(
- "SELECT id, title, tags, practice_focus, video_url, difficulty, progress_status,
- note_text, is_featured, sort_order, status
- FROM dance_practice_items
- WHERE status IN ('published', 'coming_soon')
- ORDER BY sort_order, id"
- )->fetchAll();
-} catch (PDOException) {
- http_response_code(503);
- echo json_encode(['ok' => false, 'items' => []], JSON_UNESCAPED_UNICODE);
- exit;
-}
-
-$difficultyLabels = ['easy' => '入門', 'medium' => '中等', 'hard' => '挑戰'];
-$progressLabels = ['not_started' => '未開始', 'practicing' => '練習中', 'review' => '待複習', 'completed' => '已完成'];
-
-$items = array_map(static function (array $row) use ($difficultyLabels, $progressLabels): array {
- $videoUrl = trim((string) ($row['video_url'] ?? ''));
- if ($videoUrl !== '' && (!filter_var($videoUrl, FILTER_VALIDATE_URL) || !preg_match('/^https?:\/\//i', $videoUrl))) {
- $videoUrl = '';
- }
- return [
- 'id' => (int) $row['id'],
- 'title' => (string) $row['title'],
- 'tags' => array_values(array_filter(array_map('trim', explode(',', (string) ($row['tags'] ?? ''))))),
- 'practiceFocus' => (string) $row['practice_focus'],
- 'videoUrl' => $videoUrl,
- 'difficulty' => $difficultyLabels[$row['difficulty']] ?? (string) $row['difficulty'],
- 'progress' => $progressLabels[$row['progress_status']] ?? (string) $row['progress_status'],
- 'note' => (string) ($row['note_text'] ?? ''),
- 'featured' => (bool) $row['is_featured'],
- 'comingSoon' => $row['status'] === 'coming_soon',
- ];
-}, $rows);
-
-echo json_encode(['ok' => true, 'items' => $items], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);