diff --git a/app/Console/Commands/AttendanceCleanup.php b/app/Console/Commands/AttendanceCleanup.php index 0b800863..9eed9e9a 100644 --- a/app/Console/Commands/AttendanceCleanup.php +++ b/app/Console/Commands/AttendanceCleanup.php @@ -3,6 +3,7 @@ namespace App\Console\Commands; use App\Models\Attendance; +use App\Repositories\AttendanceRepository; use Illuminate\Console\Command; class AttendanceCleanup extends Command @@ -33,39 +34,16 @@ class AttendanceCleanup extends Command /** * Execute the console command. - * - * @return int */ public function handle() { - $query = Attendance::query()->groupBy('uid')->selectRaw('uid, max(id) as max_id'); - $page = 1; - $size = 10000; - while (true) { - $rows = $query->forPage($page, $size)->get(); - $log = "sql: " . last_query() . ", count: " . $rows->count(); - do_log($log); - $this->info($log); - if ($rows->isEmpty()) { - $log = "no more data...."; - do_log($log); - $this->info($log); - break; - } - foreach ($rows as $row) { - do { - $deleted = Attendance::query() - ->where('uid', $row->uid) - ->where('id', '<', $row->max_id) - ->limit(10000) - ->delete(); - $log = "delete: $deleted by sql: " . last_query(); - do_log($log); - $this->info($log); - } while ($deleted > 0); - } - $page++; - } - return 0; + $rep = new AttendanceRepository(); + $result = $rep->cleanup(); + $log = sprintf( + '[%s], %s, result: %s', + nexus()->getRequestId(), __METHOD__, var_export($result, true) + ); + $this->info($log); + do_log($log); } } diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index 785fc8dd..fc82bdaa 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -70,21 +70,21 @@ class Test extends Command */ public function handle() { - $searchRep = new SearchRepository(); - $r = $searchRep->deleteIndex(); - $r = $searchRep->createIndex(); - $r = $searchRep->import(); - - $arr = [ - 'cat' => 'category', - 'source' => 'source', - 'medium' => 'medium', - 'codec' => 'codec', - 'audiocodec' => 'audiocodec', - 'standard' => 'standard', - 'processing' => 'processing', - 'team' => 'team', - ]; +// $searchRep = new SearchRepository(); +// $r = $searchRep->deleteIndex(); +// $r = $searchRep->createIndex(); +// $r = $searchRep->import(); +// +// $arr = [ +// 'cat' => 'category', +// 'source' => 'source', +// 'medium' => 'medium', +// 'codec' => 'codec', +// 'audiocodec' => 'audiocodec', +// 'standard' => 'standard', +// 'processing' => 'processing', +// 'team' => 'team', +// ]; $queryString = 'cat401=1&cat404=1&source2=1&medium2=1&medium3=1&codec3=1&audiocodec3=1&standard2=1&standard3=1&processing2=1&team3=1&team4=1&incldead=1&spstate=0&inclbookmarked=0&search=&search_area=0&search_mode=0'; $userSetting = '[cat401][cat404][sou1][med1][cod1][sta2][sta3][pro2][tea2][aud2][incldead=0][spstate=3][inclbookmarked=2]'; // foreach ($arr as $queryField => $value) { @@ -116,6 +116,11 @@ class Test extends Command // $r = $searchRep->deleteBookmark(1); // $r = $searchRep->addBookmark(1); + $rep = new AttendanceRepository(); + $uid = 1; + $attendance = $rep->getAttendance($uid); +// $r = $rep->migrateAttendanceLogs($uid); + $r = $rep->getContinuousDays($attendance); dd($r); } diff --git a/app/Models/Attendance.php b/app/Models/Attendance.php index 001e8482..17d50c65 100644 --- a/app/Models/Attendance.php +++ b/app/Models/Attendance.php @@ -21,4 +21,10 @@ class Attendance extends NexusModel 30 => 1000 ]; + + public function logs(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(AttendanceLog::class, 'uid', 'uid'); + } + } diff --git a/app/Models/AttendanceLog.php b/app/Models/AttendanceLog.php new file mode 100644 index 00000000..ba9728f6 --- /dev/null +++ b/app/Models/AttendanceLog.php @@ -0,0 +1,13 @@ + ['text' => 'Peasant'], self::CLASS_USER => ['text' => 'User', 'min_seed_points' => 0], self::CLASS_POWER_USER => ['text' => 'Power User', 'min_seed_points' => 40000], diff --git a/app/Repositories/AttendanceRepository.php b/app/Repositories/AttendanceRepository.php index 06d193a7..93c25282 100644 --- a/app/Repositories/AttendanceRepository.php +++ b/app/Repositories/AttendanceRepository.php @@ -2,6 +2,7 @@ namespace App\Repositories; use App\Models\Attendance; +use App\Models\AttendanceLog; use App\Models\Setting; use App\Models\User; use Carbon\Carbon; @@ -16,9 +17,6 @@ class AttendanceRepository extends BaseRepository $today = Carbon::today(); $settings = Setting::get('bonus'); $initialBonus = $settings['attendance_initial'] ?? Attendance::INITIAL_BONUS; - $stepBonus = $settings['attendance_step'] ?? Attendance::STEP_BONUS; - $maxBonus = $settings['attendance_max'] ?? Attendance::MAX_BONUS; - $continuousBonus = $settings['attendance_continuous'] ?? Attendance::CONTINUOUS_BONUS; $isUpdated = 1; $initialData = [ 'uid' => $uid, @@ -43,11 +41,12 @@ class AttendanceRepository extends BaseRepository if ($diffDays == 1) { //yesterday do it, it's continuous do_log("[CONTINUOUS]"); - $points = $this->getContinuousPoints($initialBonus, $stepBonus, $attendance->days, $maxBonus, $continuousBonus); + $continuousDays = $this->getContinuousDays($attendance); + $points = $this->getContinuousPoints($continuousDays); $update = [ 'added' => $now, 'points' => $points, - 'days' => $attendance->days + 1, + 'days' => $continuousDays + 1, 'total_days' => $attendance->total_days + 1, ]; } else { @@ -61,6 +60,12 @@ class AttendanceRepository extends BaseRepository } if ($isUpdated) { User::query()->where('id', $uid)->increment('seedbonus', $update['points']); + $attendanceLog = [ + 'uid' => $attendance->uid, + 'points' => $update['points'], + 'date' => $now->format('Y-m-d'), + ]; + AttendanceLog::query()->insert($attendanceLog); } $attendance->added_time = $now->toTimeString(); $attendance->is_updated = $isUpdated; @@ -81,8 +86,13 @@ class AttendanceRepository extends BaseRepository return $query->first(); } - private function getContinuousPoints($initial, $step, $days, $max, $extraAwards) + private function getContinuousPoints($days) { + $settings = Setting::get('bonus'); + $initial = $settings['attendance_initial'] ?? Attendance::INITIAL_BONUS; + $step = $settings['attendance_step'] ?? Attendance::STEP_BONUS; + $max = $settings['attendance_max'] ?? Attendance::MAX_BONUS; + $extraAwards = $settings['attendance_continuous'] ?? Attendance::CONTINUOUS_BONUS; $points = min($initial + $days * $step, $max); krsort($extraAwards); foreach ($extraAwards as $key => $value) { @@ -94,6 +104,11 @@ class AttendanceRepository extends BaseRepository return $points; } + /** + * 将旧的 1 人 1 天 1 条迁移到新版 1 人一条 + * + * @return int + */ public function migrateAttendance(): int { $page = 1; @@ -137,4 +152,174 @@ class AttendanceRepository extends BaseRepository return count($idArr); } + + /** + * 清理签到记录,每人只保留一条 + * + * @return int + */ + public function cleanup(): int + { + $query = Attendance::query()->groupBy('uid')->havingRaw("count(*) > 1")->selectRaw('uid, max(id) as max_id'); + $page = 1; + $size = 10000; + $deleteCounts = 0; + while (true) { + $rows = $query->forPage($page, $size)->get(); + $log = "sql: " . last_query() . ", count: " . $rows->count(); + do_log($log, 'info', app()->runningInConsole()); + if ($rows->isEmpty()) { + $log = "no more data...."; + do_log($log, 'info', app()->runningInConsole()); + break; + } + foreach ($rows as $row) { + do { + $deleted = Attendance::query() + ->where('uid', $row->uid) + ->where('id', '<', $row->max_id) + ->limit(10000) + ->delete(); + $log = "delete: $deleted by sql: " . last_query(); + $deleteCounts += $deleted; + do_log($log, 'info', app()->runningInConsole()); + } while ($deleted > 0); + } + $page++; + } + return $deleteCounts; + } + + /** + * 为 1.7 新的补签功能回写当前连续签到记录 + * + * @param int $uid + * @return int + */ + public function migrateAttendanceLogs($uid = 0): int + { + $cleanUpCounts = $this->cleanup(); + do_log("cleanup count: $cleanUpCounts", 'info', app()->runningInConsole()); + + $page = 1; + $size = 10000; + $insert = []; + $table = 'attendance_logs'; + while (true) { + $logPrefix = "[MIGRATE_ATTENDANCE_LOGS], page: $page, size: $size"; + $query = Attendance::query() + ->where('added', '>=', Carbon::yesterday()) + ->forPage($page, $size); + if ($uid) { + $query->where('uid', $uid); + } + $result = $query->get(); + do_log("$logPrefix, " . last_query() . ", count: " . $result->count(), 'info', app()->runningInConsole()); + if ($result->isEmpty()) { + do_log("$logPrefix, no more data..."); + break; + } + foreach ($result as $row) { + $interval =\DateInterval::createFromDateString("-1 day"); + $period = new \DatePeriod($row->added->addDay(1), $interval, $row->days, \DatePeriod::EXCLUDE_START_DATE); + $i = 0; + foreach ($period as $periodValue) { + $insert[] = [ + 'uid' => $row->uid, + 'points' => ($i == 0 ? $row->points : 0), + 'date' => $periodValue->format('Y-m-d'), + ]; + $i++; + } + } + $page++; + } + if (empty($insert)) { + do_log("no data to insert...", 'info', app()->runningInConsole()); + return 0; + } + NexusDB::table($table)->insert($insert); + $insertCount = count($insert); + do_log("[MIGRATE_ATTENDANCE_LOGS] DONE! insert count: " . $insertCount, 'info', app()->runningInConsole()); + + return $insertCount; + } + + public function getContinuousDays(Attendance $attendance, $start = ''): int + { + if (!empty($start)) { + $start = Carbon::parse($start); + } else { + $start = $attendance->added; + } + $logQuery = $attendance->logs()->where('created_at', '<=', $start)->orderBy('date', 'desc'); + $attendanceLogs = $logQuery->get(['date'])->keyBy('date'); + $counts = $attendanceLogs->count(); + do_log(sprintf('user: %s, log counts: %s from query: %s', $attendance->uid, $counts, last_query())); + if ($counts == 0) { + return 0; + } + $interval =\DateInterval::createFromDateString("-1 day"); + $period = new \DatePeriod($start->clone()->addDay(1), $interval, $counts, \DatePeriod::EXCLUDE_START_DATE); + $days = 0; + foreach ($period as $value) { + $checkDate = $value->format('Y-m-d'); + if ($attendanceLogs->has($checkDate)) { + $days++; + do_log(sprintf('user: %s, date: %s, has attendance, now days: %s', $attendance->uid, $checkDate, $days)); + } else { + do_log(sprintf('user: %s, date: %s, not attendance, now days: %s', $attendance->uid, $checkDate, $days)); + break; + } + } + return $days; + + } + + public function retroactive($user, $timestampMs) + { + if (!$user instanceof User) { + $user = User::query()->findOrFail((int)$user); + } + $attendance = $this->getAttendance($user->id); + if (!$attendance) { + throw new \LogicException("Haven't attendance yet"); + } + $date = Carbon::createFromTimestampMs($timestampMs); + return NexusDB::transaction(function () use ($user, $attendance, $date) { + if (AttendanceLog::query()->where('uid', $user->id)->where('date', $date->format('Y-m-d'))->exists()) { + throw new \RuntimeException("Already attendance"); + } + if ($user->attendance_card < 1) { + throw new \RuntimeException("Attendance card not enough"); + } + $log = sprintf('user: %s, card: %s, retroactive date: %s', $user->id, $user->attendance_card, $date->format('Y-m-d')); + $continuousDays = $this->getContinuousDays($attendance, $date->clone()->subDay(1)); + $log .= ", continuousDays: $continuousDays"; + $points = $this->getContinuousPoints($continuousDays); + $log .= ", points: $points"; + do_log($log); + $userUpdates = [ + 'attendance_card' => NexusDB::raw('attendance_card - 1'), + 'seedbonus' => NexusDB::raw("seedbonus + $points"), + ]; + $affectedRows = User::query() + ->where('id', $user->id) + ->where('attendance_card', $user->attendance_card) + ->update($userUpdates); + $msg = "Decrement user attendance_card and increment bonus"; + if ($affectedRows != 1) { + do_log("$msg fail, query: " . last_query()); + throw new \RuntimeException("$msg fail"); + } + do_log("$msg success, query: " . last_query()); + $insert = [ + 'uid' => $user->id, + 'points' => $points, + 'date' => $date, + 'is_retroactive' => 1, + ]; + return AttendanceLog::query()->create($insert); + }); + } } diff --git a/app/Repositories/SearchRepository.php b/app/Repositories/SearchRepository.php index 8cda8f55..3628a11d 100644 --- a/app/Repositories/SearchRepository.php +++ b/app/Repositories/SearchRepository.php @@ -71,6 +71,9 @@ class SearchRepository extends BaseRepository //tag 'tag_id' => ['type' => 'long', ], + //use for category.mode + 'mode' => ['type' => 'long', ], + //relations 'torrent_relations' => [ 'type' => 'join', @@ -194,7 +197,7 @@ class SearchRepository extends BaseRepository $fields = $this->getTorrentBaseFields(); array_unshift($fields, 'id'); $query = Torrent::query() - ->with(['user', 'torrent_tags', 'bookmarks']) + ->with(['user', 'torrent_tags', 'bookmarks', 'basic_category']) ->select($fields); if (!is_null($torrentId)) { $idArr = preg_split('/[,\s]+/', $torrentId); @@ -299,6 +302,7 @@ class SearchRepository extends BaseRepository 'routing' => $torrent->owner, ]; $data = Arr::only($torrent->toArray(), $baseFields); + $data['mode'] = $torrent->basic_category->mode; $body = array_merge($data, [ '_doc_type' => $docType, 'torrent_id' => $torrent->id, @@ -433,6 +437,9 @@ class SearchRepository extends BaseRepository $must = $must_not = []; $mustBoolShould = []; $must[] = ['match' => ['_doc_type' => self::DOC_TYPE_TORRENT]]; + if (!empty($params['mode'])) { + $must[] = ['match' => ['mode' => $params['mode']]]; + } foreach (self::$queryFieldToTorrentFieldMaps as $queryField => $torrentField) { if (isset($params[$queryField]) && $params[$queryField] !== '') { diff --git a/database/migrations/2022_04_02_163930_create_attendance_logs_table.php b/database/migrations/2022_04_02_163930_create_attendance_logs_table.php new file mode 100644 index 00000000..3e782cf6 --- /dev/null +++ b/database/migrations/2022_04_02_163930_create_attendance_logs_table.php @@ -0,0 +1,36 @@ +id(); + $table->integer('uid')->index(); + $table->integer('points'); + $table->date('date')->index(); + $table->smallInteger('is_retroactive')->default(0); + $table->dateTime('created_at')->useCurrent(); + $table->dateTime('updated_at')->useCurrent()->useCurrentOnUpdate(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('attendance_logs'); + } +}; diff --git a/database/migrations/2022_04_03_041642_add_attendance_card_to_users_table.php b/database/migrations/2022_04_03_041642_add_attendance_card_to_users_table.php new file mode 100644 index 00000000..7decc7f0 --- /dev/null +++ b/database/migrations/2022_04_03_041642_add_attendance_card_to_users_table.php @@ -0,0 +1,32 @@ +integer('attendance_card')->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('attendance_card'); + }); + } +}; diff --git a/include/functions.php b/include/functions.php index f28b98fc..e3274b78 100644 --- a/include/functions.php +++ b/include/functions.php @@ -2375,6 +2375,11 @@ if ($CURUSER){ + @@ -2490,7 +2495,7 @@ else { = UC_SYSOP) { ?> [] [] []: - points); }else{ printf(' %s', $lang_functions['text_attendance']);}?> + '.$lang_functions['text_attended'].'', $attendance->points, $CURUSER['attendance_card']); }else{ printf(' %s', $lang_functions['text_attendance']);}?> : []:
@@ -2699,6 +2704,9 @@ function stdfoot() { print(""); if ($analyticscode_tweak) print("\n".$analyticscode_tweak."\n"); + foreach (\Nexus\Nexus::getAppendFooters() as $value) { + print($value); + } print(""); //echo replacePngTags(ob_get_clean()); diff --git a/lang/chs/lang_attendance.php b/lang/chs/lang_attendance.php index c53f5ba3..97d91f6f 100644 --- a/lang/chs/lang_attendance.php +++ b/lang/chs/lang_attendance.php @@ -10,4 +10,7 @@ $lang_attendance = array 'continuous' => "连续签到 %u 天后,每次签到额外获得 %u 魔力值(不累计)。", 'sorry' => "抱歉", 'already_attended' => "您今天已经签到过了,请勿重复刷新。", + 'retroactive_event_text' => '补', + 'retroactive_confirm_tip' => '确定要补签: ', + 'retroactive_description' => '点击白色背景的圆点进行补签。你目前拥有补签卡 %d 张。', ); diff --git a/lang/chs/lang_functions.php b/lang/chs/lang_functions.php index 9deb0dc5..cbe0bb7f 100644 --- a/lang/chs/lang_functions.php +++ b/lang/chs/lang_functions.php @@ -296,7 +296,7 @@ $lang_functions = array 'text_please_download_something_within' => "请在", 'text_inactive_account_be_deleted' => "内做些下载。没有流量的用户会被删除账号。", 'text_attendance' => '签到得魔力', - 'text_attended' => '(签到已得%u)', + 'text_attended' => '(签到已得%u, 补签卡:%d)', 'row_pt_gen_douban_url' => "PT-Gen douban 链接", 'text_pt_gen_douban_url_note' => "(来自 douban 的链接。如电影 Transformers 的链接是 https://movie.douban.com/subject/1794171/)", 'row_pt_gen_imdb_url' => "PT-Gen imdb 链接", diff --git a/lang/cht/lang_functions.php b/lang/cht/lang_functions.php index 08333451..529b9f7d 100644 --- a/lang/cht/lang_functions.php +++ b/lang/cht/lang_functions.php @@ -298,7 +298,7 @@ $lang_functions = array 'text_please_download_something_within' => "請在", 'text_inactive_account_be_deleted' => "內做些下載。沒有流量的用戶會被移除賬號。", 'text_attendance' => '簽到得魔力', - 'text_attended' => '(簽到已得%u)', + 'text_attended' => '(簽到已得%u, 補簽卡:%d)', 'text_pt_gen_douban_url_note' => "(來自 douban 的鏈接。如電影 Transformers 的鏈接是 https://movie.douban.com/subject/1794171/)", 'row_pt_gen_imdb_url' => "PT-Gen imdb 鏈接", 'text_pt_gen_imdb_url_note' => "(來自 imdb 的鏈接。如電影 Transformers 的鏈接是 https://www.imdb.com/title/tt0418279/)", diff --git a/lang/en/lang_functions.php b/lang/en/lang_functions.php index 1cdd0cb5..0f027bf8 100644 --- a/lang/en/lang_functions.php +++ b/lang/en/lang_functions.php @@ -298,7 +298,7 @@ $lang_functions = array 'text_please_download_something_within' => "Please download something within ", 'text_inactive_account_be_deleted' => ". Inactive accounts (with no transfer amount) will be deleted.", 'text_attendance' => 'Attend get bouns', - 'text_attended' => '(Attend got bouns %u)', + 'text_attended' => '(Attend got bouns %u, card:%d)', 'row_pt_gen_douban_url' => "PT-Gen douban link", 'text_pt_gen_douban_url_note' => "(URL taken from douban. e.g. for movie Transformers the URL is https://movie.douban.com/subject/1794171//)", 'row_pt_gen_imdb_url' => "PT-Gen imdb link", diff --git a/nexus/Install/Update.php b/nexus/Install/Update.php index 16b8c6b2..9215e54b 100644 --- a/nexus/Install/Update.php +++ b/nexus/Install/Update.php @@ -198,6 +198,19 @@ class Update extends Install } } + /** + * @since 1.7.0 + * + * add attendance_card to users + */ + if (WITH_LARAVEL && !NexusDB::schema()->hasColumn('users', 'attendance_card')) { + $this->runMigrate('database/migrations/2022_04_02_163930_create_attendance_logs_table.php'); + $this->runMigrate('database/migrations/2022_04_03_041642_add_attendance_card_to_users_table.php'); + $rep = new AttendanceRepository(); + $count = $rep->migrateAttendanceLogs(); + $this->doLog("[ADD_ATTENDANCE_CARD_TO_USERS], migrateAttendanceLogs: $count"); + } + } public function runExtraMigrate() diff --git a/nexus/Nexus.php b/nexus/Nexus.php index ebbf0d92..b068c687 100644 --- a/nexus/Nexus.php +++ b/nexus/Nexus.php @@ -17,6 +17,10 @@ final class Nexus private static ?Nexus $instance = null; + private static array $appendHeaders = []; + + private static array $appendFooters = []; + const PLATFORM_USER = 'user'; const PLATFORM_ADMIN = 'admin'; const PLATFORM_TRACKER = 'tracker'; @@ -179,5 +183,48 @@ final class Nexus $this->platform = $platform; } + public static function js(string $js, string $position, bool $isFile) + { + if ($isFile) { + $append = sprintf('', $js); + } else { + $append = sprintf('', $js); + } + if ($position == 'header') { + self::$appendHeaders[] = $append; + } elseif ($position == 'footer') { + self::$appendFooters[] = $append; + } else { + throw new \InvalidArgumentException("Invalid position: $position"); + } + } + + public static function css(string $css, string $position, bool $isFile) + { + if ($isFile) { + $append = sprintf('', $css); + } else { + $append = sprintf('', $css); + } + if ($position == 'header') { + self::$appendHeaders[] = $append; + } elseif ($position == 'footer') { + self::$appendFooters[] = $append; + } else { + throw new \InvalidArgumentException("Invalid position: $position"); + } + } + + public static function getAppendHeaders(): array + { + return self::$appendHeaders; + } + + public static function getAppendFooters(): array + { + return self::$appendFooters; + } + + } diff --git a/public/ajax.php b/public/ajax.php index 31060c7b..0c8098e6 100644 --- a/public/ajax.php +++ b/public/ajax.php @@ -27,3 +27,10 @@ function toggleUserMedalStatus($params) } +function attendanceRetroactive($params) +{ + global $CURUSER; + $rep = new \App\Repositories\AttendanceRepository(); + return $rep->retroactive($CURUSER['id'], $params['timestamp']); +} + diff --git a/public/amountattendancecard.php b/public/amountattendancecard.php new file mode 100644 index 00000000..f38fdf42 --- /dev/null +++ b/public/amountattendancecard.php @@ -0,0 +1,65 @@ + +
+
+

Add attendance card to all staff members and users:

+
> + + "> + + + + + + + + + + + + + + +
Upload amount has been added and inform message has been sent.
Amount
Add to + + + + $value) {?> + + + + + +
+
+
Subject
Reason
Operator:   + + +   System + +
+ > +
+ +
+
+ NOTE: Do not use BB codes. (NO HTML) +clone()->endOfMonth(); +$start = $today->clone()->subMonth(2); $rep = new \App\Repositories\AttendanceRepository(); -$result = $rep->attend($CURUSER['id']); -if ($result->is_updated) { - $count = $result->total_days; - $cdays = $result->days; - $points = $result->points; +$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); +$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']); + } + } else { + $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->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']); - printf('

'.$lang_attendance['attend_info'].'

', $count, $cdays, $points); + printf('

'.$lang_attendance['attend_info'].$lang_attendance['retroactive_description'].'

', $count, $cdays, $points, $CURUSER['attendance_card']); end_frame(); + echo '
'; echo '