diff --git a/.env.example b/.env.example
index cbd1aa08..c3588b1b 100644
--- a/.env.example
+++ b/.env.example
@@ -99,6 +99,7 @@ FORCE_SCHEME=
# Captcha settings
# Available drivers: image, cloudflare_turnstile, google_recaptcha_v2
CAPTCHA_DRIVER=image
+CAPTCHA_ATTENDANCE_ENABLED=true
# Cloudflare Turnstile keys (used when CAPTCHA_DRIVER=cloudflare_turnstile)
TURNSTILE_SITE_KEY=
diff --git a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php
index 97ca00b6..9d3e3a31 100644
--- a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php
+++ b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php
@@ -13,6 +13,7 @@ use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\CheckboxList;
use Filament\Schemas\Components\Fieldset;
use Filament\Forms\Components\Repeater;
+use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Section;
use App\Auth\Permission;
@@ -30,6 +31,7 @@ use Filament\Facades\Filament;
use Filament\Resources\Pages\Page;
use Filament\Forms;
use Illuminate\Support\HtmlString;
+use Illuminate\Support\Arr;
use Meilisearch\Contracts\Index\Settings;
use Nexus\Database\NexusDB;
@@ -64,6 +66,16 @@ class EditSetting extends Page implements HasForms
private function fillForm()
{
$settings = Setting::getFromDb();
+
+ $fallbackEnabled = (bool) config('captcha.attendance.enabled', true);
+ $rawSetting = Arr::get($settings, 'captcha.attendance.enabled', $fallbackEnabled);
+ if (is_string($rawSetting)) {
+ $normalized = in_array(strtolower($rawSetting), ['1', 'true', 'yes'], true);
+ } else {
+ $normalized = (bool) $rawSetting;
+ }
+ Arr::set($settings, 'captcha.attendance.enabled', $normalized);
+
$this->form->fill($settings);
}
@@ -298,7 +310,20 @@ class EditSetting extends Page implements HasForms
Setting::get('captcha.recaptcha.size', nexus_env('RECAPTCHA_SIZE', 'normal'))
);
+ $attendanceCaptchaSetting = Setting::get('captcha.attendance.enabled', true);
+ if (is_string($attendanceCaptchaSetting)) {
+ $attendanceCaptchaEnabled = in_array(strtolower($attendanceCaptchaSetting), ['1', 'true', 'yes'], true);
+ } else {
+ $attendanceCaptchaEnabled = (bool) $attendanceCaptchaSetting;
+ }
+
$schema = [
+ Toggle::make("$captchaPrefix.attendance.enabled")
+ ->label(__('label.setting.captcha.attendance.enabled'))
+ ->helperText(__('label.setting.captcha.attendance.enabled_help'))
+ ->default($attendanceCaptchaEnabled)
+ ->columnSpanFull()
+ ,
Select::make("$captchaPrefix.default")
->options($driverOptions)
->label(__('label.setting.captcha.driver'))
diff --git a/config/captcha.php b/config/captcha.php
index 021a45ad..7cd90cc9 100644
--- a/config/captcha.php
+++ b/config/captcha.php
@@ -24,4 +24,8 @@ return [
'size' => nexus_env('RECAPTCHA_SIZE', 'normal'),
],
],
+
+ 'attendance' => [
+ 'enabled' => nexus_env('CAPTCHA_ATTENDANCE_ENABLED', true),
+ ],
];
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..67b4325e 100644
--- a/public/attendance.php
+++ b/public/attendance.php
@@ -1,32 +1,10 @@
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 '';
-// printf('- '.$lang_attendance['initial'].'
', $attendance_initial_bonus);
-// printf('- '.$lang_attendance['steps'].'
', $attendance_step_bonus, $attendance_max_bonus);
-// echo '';
-// foreach($attendance_continuous_bonus as $day => $value){
-// printf('- '.$lang_attendance['continuous'].'
', $day, $value);
-// }
-// echo '
';
-// 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);
@@ -44,38 +22,106 @@ $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']);
- }
+$attendanceRepository = new \App\Repositories\AttendanceRepository();
+
+$attendanceCaptchaSetting = \App\Models\Setting::get('captcha.attendance.enabled', config('captcha.attendance.enabled', true));
+if (is_string($attendanceCaptchaSetting)) {
+ $attendanceCaptchaEnabled = in_array(strtolower($attendanceCaptchaSetting), ['1', 'true', 'yes'], true);
+} else {
+ $attendanceCaptchaEnabled = (bool) $attendanceCaptchaSetting;
}
-$eventStr = json_encode($events);
-$validRangeStr = json_encode(['start' => $start->format('Y-m-d'), 'end' => $end->clone()->addDays(1)->format('Y-m-d')]);
-$js = <<attend($CURUSER['id']);
+ if (!$attendance->is_updated) {
+ stderr($lang_attendance['sorry'], $lang_attendance['already_attended']);
+ }
+} else {
+ $attendance = $attendanceRepository->getAttendance($CURUSER['id']);
+}
+
+$hasAttendedToday = $attendance && $attendance->added && $attendance->added->isSameDay($today);
+
+if (!$attendanceCaptchaEnabled && !$hasAttendedToday) {
+ $attendance = $attendanceRepository->attend($CURUSER['id']);
+ $hasAttendedToday = $attendance && $attendance->added && $attendance->added->isSameDay($today);
+}
+
+if (!$attendance) {
+ $attendance = new \App\Models\Attendance([
+ 'uid' => $CURUSER['id'],
+ 'points' => 0,
+ 'days' => 0,
+ 'total_days' => 0,
+ ]);
+ $attendance->added = null;
+ $hasAttendedToday = false;
+}
+
+stdhead($lang_attendance['title']);
+begin_main_frame();
+
+if ($hasAttendedToday) {
+ $todayDate = $today->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')]);
+
+ $calendarScript = <<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);
printf('- '.$lang_attendance['steps'].'
', $attendance_step_bonus, $attendance_max_bonus);
echo '';
- foreach($attendance_continuous_bonus as $day => $value){
+ foreach ($attendance_continuous_bonus as $day => $value) {
printf('- '.$lang_attendance['continuous'].'
', $day, $value);
}
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 '
';
+ end_frame();
}
+
+end_main_frame();
+stdfoot();
diff --git a/resources/lang/en/label.php b/resources/lang/en/label.php
index 7d5f42e7..376180bc 100644
--- a/resources/lang/en/label.php
+++ b/resources/lang/en/label.php
@@ -144,6 +144,10 @@ return [
'size_normal' => 'Normal',
'size_compact' => 'Compact',
],
+ 'attendance' => [
+ 'enabled' => 'Require captcha for attendance check-in',
+ 'enabled_help' => 'When enabled, members must solve the captcha before signing in.',
+ ],
],
'meilisearch' => [
'tab_header' => 'Meilisearch',
diff --git a/resources/lang/zh_CN/label.php b/resources/lang/zh_CN/label.php
index c2843fba..c3632bc7 100644
--- a/resources/lang/zh_CN/label.php
+++ b/resources/lang/zh_CN/label.php
@@ -185,6 +185,10 @@ return [
'size_normal' => '普通',
'size_compact' => '紧凑',
],
+ 'attendance' => [
+ 'enabled' => '启用签到验证码',
+ 'enabled_help' => '开启后,用户每天签到前必须完成验证码验证。',
+ ],
],
'meilisearch' => [
'tab_header' => 'Meilisearch',
diff --git a/resources/lang/zh_TW/label.php b/resources/lang/zh_TW/label.php
index 210e23dc..77521adc 100644
--- a/resources/lang/zh_TW/label.php
+++ b/resources/lang/zh_TW/label.php
@@ -144,6 +144,10 @@ return [
'size_normal' => '一般',
'size_compact' => '緊湊',
],
+ 'attendance' => [
+ 'enabled' => '啟用簽到驗證碼',
+ 'enabled_help' => '啟用後,使用者簽到前必須完成驗證碼驗證。',
+ ],
],
'meilisearch' => [
'tab_header' => 'Meilisearch',