From 908b4b19ea1df7aa065772e98213ddcb9d84ce53 Mon Sep 17 00:00:00 2001 From: Qi HU Date: Mon, 13 Oct 2025 15:57:41 +0800 Subject: [PATCH] feat(attendance): enforce captcha validation before check-in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - require a successful captcha challenge before recording the day’s attendance - preserve the original attendance summary layout after a successful check-in Signed-off-by: Qi HU --- lang/chs/lang_attendance.php | 1 + lang/cht/lang_attendance.php | 1 + lang/en/lang_attendance.php | 1 + public/attendance.php | 177 ++++++++++++++++++++--------------- 4 files changed, 106 insertions(+), 74 deletions(-) diff --git a/lang/chs/lang_attendance.php b/lang/chs/lang_attendance.php index dd9d617b..46e9a9bc 100644 --- a/lang/chs/lang_attendance.php +++ b/lang/chs/lang_attendance.php @@ -13,4 +13,5 @@ $lang_attendance = array 'retroactive_event_text' => '补', 'retroactive_confirm_tip' => '确定要补签: ', 'retroactive_description' => '点击白色背景的圆点进行补签。你目前拥有补签卡 %d 张。', + 'attend_button' => '立即签到', ); diff --git a/lang/cht/lang_attendance.php b/lang/cht/lang_attendance.php index 1b6db754..41eaa9c3 100644 --- a/lang/cht/lang_attendance.php +++ b/lang/cht/lang_attendance.php @@ -13,4 +13,5 @@ $lang_attendance = array 'retroactive_event_text' => '補', 'retroactive_confirm_tip' => '確定要補簽: ', 'retroactive_description' => '點擊白色背景的圓點進行補簽。你目前擁有補簽卡 %d 張。', + 'attend_button' => '立即簽到', ); diff --git a/lang/en/lang_attendance.php b/lang/en/lang_attendance.php index c5faadde..3899c91d 100644 --- a/lang/en/lang_attendance.php +++ b/lang/en/lang_attendance.php @@ -13,4 +13,5 @@ $lang_attendance = array 'retroactive_event_text' => 'Re', 'retroactive_confirm_tip' => 'Confirm to attend: ', 'retroactive_description' => 'Click on the dot on the white background to do attend. You currently have a attendance card %d.', + 'attend_button' => 'Check in now', ); diff --git a/public/attendance.php b/public/attendance.php index efb5551b..dbb26f48 100644 --- a/public/attendance.php +++ b/public/attendance.php @@ -4,33 +4,6 @@ dbconn(); require get_langfile_path(); loggedinorreturn(); parked(); -//$desk = new Attendance($CURUSER['id']); -// -//if($result = $desk->attend($attendance_initial_bonus, $attendance_step_bonus, $attendance_max_bonus, $attendance_continuous_bonus)){ -// list($count, $cdays, $points) = $result; -// stdhead($lang_attendance['title']); -// begin_main_frame(); -// begin_frame($lang_attendance['success']); -// printf('

'.$lang_attendance['attend_info'].'

', $count, $cdays, $points); -// end_frame(); -// echo ''; -// end_main_frame(); -// stdfoot(); -//}else{ -// stderr($lang_attendance['sorry'], $lang_attendance['already_attended']); -//} - -\Nexus\Nexus::css('vendor/fullcalendar-5.10.2/main.min.css', 'header', true); -\Nexus\Nexus::js('vendor/fullcalendar-5.10.2/main.min.js', 'footer', true); - $lang = get_langfolder_cookie(); $localesMap = [ 'en' => 'en-us', @@ -38,44 +11,99 @@ $localesMap = [ 'cht' => 'zh-tw', ]; $localeJs = $localesMap[$lang] ?? 'en-us'; + +\Nexus\Nexus::css('vendor/fullcalendar-5.10.2/main.min.css', 'header', true); +\Nexus\Nexus::js('vendor/fullcalendar-5.10.2/main.min.js', 'footer', true); \Nexus\Nexus::js("vendor/fullcalendar-5.10.2/locales/{$localeJs}.js", 'footer', true); +$rep = new \App\Repositories\AttendanceRepository(); + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if ($iv == "yes") { + check_code($_POST['imagehash'] ?? null, $_POST['imagestring'] ?? null, 'attendance.php'); + } + $attendance = $rep->attend($CURUSER['id']); + if (!$attendance->is_updated) { + stderr($lang_attendance['sorry'], $lang_attendance['already_attended']); + } +} else { + $attendance = $rep->getAttendance($CURUSER['id']); + if (!$attendance) { + $attendance = new \App\Models\Attendance([ + 'uid' => $CURUSER['id'], + 'points' => 0, + 'days' => 0, + 'total_days' => 0, + ]); + $attendance->added = null; + } +} + $today = \Carbon\Carbon::today(); $tomorrow = \Carbon\Carbon::tomorrow(); $end = $today->clone()->endOfMonth(); $start = $today->clone()->subMonth(2); -$rep = new \App\Repositories\AttendanceRepository(); -$attendance = $rep->attend($CURUSER['id']); -$logs = $attendance->logs()->where('date', '>=', $start->format('Y-m-d'))->get()->keyBy('date'); -$interval = new \DateInterval('P1D'); -$period = new \DatePeriod($start, $interval, $end); -$interval = \Carbon\CarbonInterval::make($interval); -$period = \Carbon\CarbonPeriod::make($period); -$events = []; -foreach ($period as $value) { - if ($value->gte($tomorrow)) { - continue; - } - $checkDate = $value->format('Y-m-d'); - $eventBase = ['start' => $checkDate, 'end' => $checkDate]; - if ($logs->has($checkDate)) { - $logValue = $logs->get($checkDate); - $events[] = array_merge($eventBase, ['display' => 'background']); - if ($logValue->points > 0) { - $events[] = array_merge($eventBase, ['title' => $logValue->points]); - } - if ($logValue->is_retroactive) { - $events[] = array_merge($eventBase, ['title' => $lang_attendance['retroactive_event_text'], 'display' => 'list-item']); - } - } elseif ($value->lte($today) && $value->diffInDays($today, true) <= \App\Models\Attendance::MAX_RETROACTIVE_DAYS) { - $events[] = array_merge($eventBase, ['groupId' => 'to_do', 'display' => 'list-item']); - } -} -$eventStr = json_encode($events); -$validRangeStr = json_encode(['start' => $start->format('Y-m-d'), 'end' => $end->clone()->addDays(1)->format('Y-m-d')]); +$hasAttendedToday = $attendance->added && $attendance->added->isSameDay($today); +stdhead($lang_attendance['title']); +begin_main_frame(); -$js = <<format('Y-m-d'); + $baseQuery = \App\Models\AttendanceLog::query()->where('date', $todayDate); + $todayCounts = $baseQuery->count(); + $myLog = (clone $baseQuery)->where('uid', $CURUSER['id'])->first(['id']); + $myRanking = 0; + if ($myLog) { + $myRanking = (clone $baseQuery)->where('id', '<=', $myLog->id)->count(); + } + + $count = $attendance->total_days; + $cdays = $attendance->days; + $points = $attendance->points; + + $headerLeft = sprintf($lang_attendance['attend_info'] . $lang_attendance['retroactive_description'], $count, $cdays, $points, $CURUSER['attendance_card']); + $headerRight = nexus_trans('attendance.ranking', ['ranking' => $myRanking, 'counts' => $todayCounts]); + + begin_frame($lang_attendance['success']); + printf('

%s%s

', $headerLeft, $headerRight); + end_frame(); + + $logs = \App\Models\AttendanceLog::query() + ->where('uid', $CURUSER['id']) + ->where('date', '>=', $start->format('Y-m-d')) + ->get() + ->keyBy('date'); + $interval = new \DateInterval('P1D'); + $period = new \DatePeriod($start, $interval, $end); + + $interval = \Carbon\CarbonInterval::make($interval); + $period = \Carbon\CarbonPeriod::make($period); + $events = []; + foreach ($period as $value) { + if ($value->gte($tomorrow)) { + continue; + } + $checkDate = $value->format('Y-m-d'); + $eventBase = ['start' => $checkDate, 'end' => $checkDate]; + if ($logs->has($checkDate)) { + $logValue = $logs->get($checkDate); + $events[] = array_merge($eventBase, ['display' => 'background']); + if ($logValue->points > 0) { + $events[] = array_merge($eventBase, ['title' => $logValue->points]); + } + if ($logValue->is_retroactive) { + $events[] = array_merge($eventBase, ['title' => $lang_attendance['retroactive_event_text'], 'display' => 'list-item']); + } + } elseif ($value->lte($today) && $value->diffInDays($today, true) <= \App\Models\Attendance::MAX_RETROACTIVE_DAYS) { + $events[] = array_merge($eventBase, ['groupId' => 'to_do', 'display' => 'list-item']); + } + } + + $eventStr = json_encode($events); + $validRangeStr = json_encode(['start' => $start->format('Y-m-d'), 'end' => $end->clone()->addDays(1)->format('Y-m-d')]); + + $js = <<total_days; - $cdays = $attendance->days; - $points = $attendance->points; - - stdhead($lang_attendance['title']); - begin_main_frame(); - begin_frame($lang_attendance['success']); - $headerLeft = sprintf($lang_attendance['attend_info'].$lang_attendance['retroactive_description'], $count, $cdays, $points, $CURUSER['attendance_card']); - $headerRight = nexus_trans('attendance.ranking', ['ranking' => $attendance->my_ranking, 'counts' => $attendance->today_counts]); - printf('

%s%s

', $headerLeft, $headerRight); - end_frame(); echo '
'; echo '
    '; printf('
  • '.$lang_attendance['initial'].'
  • ', $attendance_initial_bonus); @@ -136,9 +152,22 @@ if (1) { } echo ''; echo '
'; - end_main_frame(); - stdfoot(); - } else { - stderr($lang_attendance['sorry'], $lang_attendance['already_attended']); + $buttonLabel = $lang_attendance['attend_button'] ?? 'Check in'; + begin_frame($lang_attendance['title']); + echo ''; + echo ''; + echo '
'; + echo '
'; + echo '
'; + echo ''; + show_image_code(); + echo ''; + echo '
'; + echo '
'; + echo '
'; + echo '
'; + end_frame(); } +end_main_frame(); +stdfoot();