diff --git a/app/Filament/Resources/User/UserResource/Pages/UserProfile.php b/app/Filament/Resources/User/UserResource/Pages/UserProfile.php index 5ade4d02..be708b6d 100644 --- a/app/Filament/Resources/User/UserResource/Pages/UserProfile.php +++ b/app/Filament/Resources/User/UserResource/Pages/UserProfile.php @@ -5,6 +5,7 @@ namespace App\Filament\Resources\User\UserResource\Pages; use App\Filament\Resources\User\UserResource; use App\Models\Medal; use App\Models\User; +use App\Models\UserMeta; use App\Repositories\ExamRepository; use App\Repositories\MedalRepository; use App\Repositories\UserRepository; @@ -14,12 +15,15 @@ use Filament\Resources\Pages\Page; use Filament\Pages\Actions; use Filament\Forms; use Illuminate\Support\Facades\Auth; +use Nexus\Database\NexusDB; class UserProfile extends Page { use InteractsWithRecord; use HasRelationManagers; + private static $rep; + protected static string $resource = UserResource::class; protected static string $view = 'filament.resources.user.user-resource.pages.user-profile'; @@ -30,6 +34,14 @@ class UserProfile extends Page self::EVENT_RECORD_UPDATED => 'updateRecord' ]; + private function getRep(): UserRepository + { + if (!self::$rep) { + self::$rep = new UserRepository(); + } + return self::$rep; + } + public function updateRecord($id) { $this->record = $this->resolveRecord($id); @@ -49,8 +61,9 @@ class UserProfile extends Page { $actions = []; if (Auth::user()->class > $this->record->class) { - $actions[] = $this->buildAssignExamAction(); + $actions[] = $this->buildGrantPropsAction(); $actions[] = $this->buildGrantMedalAction(); + $actions[] = $this->buildAssignExamAction(); $actions[] = $this->buildChangeBonusEtcAction(); if ($this->record->two_step_secret) { $actions[] = $this->buildDisableTwoStepAuthenticationAction(); @@ -75,7 +88,7 @@ class UserProfile extends Page Forms\Components\Hidden::make('uid')->default($this->record->id), ]) ->action(function ($data) { - $userRep = new UserRepository(); + $userRep = $this->getRep(); try { if ($data['action'] == 'enable') { $userRep->enableUser(Auth::user(), $data['uid'], $data['reason']); @@ -96,7 +109,7 @@ class UserProfile extends Page ->modalHeading(__('admin.resources.user.actions.disable_two_step_authentication')) ->requiresConfirmation() ->action(function ($data) { - $userRep = new UserRepository(); + $userRep = $this->getRep(); try { $userRep->removeTwoStepAuthentication(Auth::user(), $this->record->id); $this->notify('success', 'Success!'); @@ -129,7 +142,7 @@ class UserProfile extends Page Forms\Components\Textarea::make('reason')->label(__('admin.resources.user.actions.change_bonus_etc_reason_label')), ]) ->action(function ($data) { - $userRep = new UserRepository(); + $userRep = $this->getRep(); try { $userRep->incrementDecrement(Auth::user(), $this->record->id, $data['action'], $data['field'], $data['value'], $data['reason']); $this->notify('success', 'Success!'); @@ -152,7 +165,7 @@ class UserProfile extends Page ->required(), ]) ->action(function ($data) { - $userRep = new UserRepository(); + $userRep = $this->getRep(); try { $userRep->resetPassword($this->record->id, $data['password'], $data['password_confirmation']); $this->notify('success', 'Success!'); @@ -241,7 +254,7 @@ class UserProfile extends Page // ->modalHeading($this->record->enabled == 'yes' ? __('admin.resources.user.actions.disable_modal_title') : __('admin.resources.user.actions.enable_modal_title')) ->requiresConfirmation() ->action(function () { - $userRep = new UserRepository(); + $userRep = $this->getRep(); try { $userRep->updateDownloadPrivileges(Auth::user(), $this->record->id, $this->record->downloadpos == 'yes' ? 'no' : 'yes'); $this->notify('success', 'Success!'); @@ -251,4 +264,57 @@ class UserProfile extends Page } }); } + + private function buildGrantPropsAction() + { + return Actions\Action::make(__('admin.resources.user.actions.grant_prop_btn')) + ->modalHeading(__('admin.resources.user.actions.grant_prop_btn')) + ->form([ + Forms\Components\Select::make('meta_key') + ->options(UserMeta::listProps()) + ->label(__('admin.resources.user.actions.grant_prop_form_prop'))->required(), + Forms\Components\TextInput::make('duration')->label(__('admin.resources.user.actions.grant_prop_form_duration')) + ->helperText(__('admin.resources.user.actions.grant_prop_form_duration_help')), + + ]) + ->action(function ($data) { + $rep = $this->getRep(); + try { + if (!empty($data['duration'])) { + $data['deadline'] = now()->addDays($data['duration']); + } + $rep->addMeta($this->record, $data); + $this->notify('success', 'Success!'); + $this->emitSelf(self::EVENT_RECORD_UPDATED, $this->record->id); + } catch (\Exception $exception) { + $this->notify('danger', $exception->getMessage()); + } + }); + } + + public function getViewData(): array + { + return [ + 'props' => $this->listUserProps(), + ]; + } + + private function listUserProps(): array + { + $metaKeys = [ + UserMeta::META_KEY_PERSONALIZED_USERNAME, + UserMeta::META_KEY_CHANGE_USERNAME, + ]; + $metaList = $this->getRep()->listMetas($this->record->id, $metaKeys); + $props = []; + foreach ($metaList as $metaKey => $metas) { + $meta = $metas->first(); + $text = sprintf('[%s]', $meta->metaKeyText, ); + if ($meta->meta_key == UserMeta::META_KEY_PERSONALIZED_USERNAME) { + $text .= sprintf('(%s)', $meta->getDeadlineText()); + } + $props[] = "
{$text}
"; + } + return $props; + } } diff --git a/app/Models/NexusModel.php b/app/Models/NexusModel.php index 7390e18d..fcead220 100644 --- a/app/Models/NexusModel.php +++ b/app/Models/NexusModel.php @@ -42,4 +42,13 @@ class NexusModel extends Model return $d && $d->format($format) === $date; } + public function getDeadlineText($field = 'deadline') + { + $raw = $this->getRawOriginal($field); + if (in_array($raw, [null, '0000-00-00 00:00:00', ''], true)) { + return nexus_trans("label.permanent"); + } + return sprintf('%s: %s', nexus_trans('label.deadline'), $raw); + } + } diff --git a/app/Models/User.php b/app/Models/User.php index 3ebd2550..4b3c18a7 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -503,5 +503,4 @@ class User extends Authenticatable implements FilamentUser, HasName } - } diff --git a/app/Models/UserMeta.php b/app/Models/UserMeta.php index 37da3c7a..975a71f0 100644 --- a/app/Models/UserMeta.php +++ b/app/Models/UserMeta.php @@ -10,7 +10,6 @@ class UserMeta extends NexusModel const STATUS_NORMAL = 0; - const META_KEY_PERSONALIZED_USERNAME = 'PERSONALIZED_USERNAME'; const META_KEY_CHANGE_USERNAME = 'CHANGE_USERNAME'; @@ -21,6 +20,19 @@ class UserMeta extends NexusModel 'deadline' => 'datetime', ]; + public static array $metaKeys = [ + self::META_KEY_PERSONALIZED_USERNAME => ['text' => 'PERSONALIZED_USERNAME', 'multiple' => false], + self::META_KEY_CHANGE_USERNAME => ['text' => 'CHANGE_USERNAME', 'multiple' => false], + ]; + + public static function listProps() + { + return [ + self::META_KEY_PERSONALIZED_USERNAME => nexus_trans('label.user_meta.meta_keys.' . self::META_KEY_PERSONALIZED_USERNAME), + self::META_KEY_CHANGE_USERNAME => nexus_trans('label.user_meta.meta_keys.' . self::META_KEY_CHANGE_USERNAME), + ]; + } + public function getMetaKeyTextAttribute() { return nexus_trans('label.user_meta.meta_keys.' . $this->meta_key) ?? ''; diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index d3c49f4b..7a1f675e 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -440,5 +440,29 @@ class UserRepository extends BaseRepository return true; } + public function addMeta($user, array $metaData, array $keyExistsUpdates = []) + { + $user = $this->getUser($user); + $metaKey = $metaData['meta_key']; + $allowMultiple = UserMeta::$metaKeys[$metaKey]['multiple']; + if ($allowMultiple) { + //Allow multiple, just insert + $result = $user->metas()->create($metaData); + } else { + $metaExists = $user->metas()->where('meta_key', $metaKey)->first(); + if (!$metaExists) { + $result = $user->metas()->create($metaData); + } else { + if (empty($keyExistsUpdates)) { + $keyExistsUpdates = ['updated_at' => now()]; + } + $result = $metaExists->update($keyExistsUpdates); + } + } + if ($result) { + clear_user_cache($user->id, $user->passkey); + } + return $result; + } } diff --git a/include/functions.php b/include/functions.php index df465d77..537fd4a9 100644 --- a/include/functions.php +++ b/include/functions.php @@ -3727,6 +3727,7 @@ function get_username($id, $big = false, $link = true, $bold = true, $target = f if ($hasSetRainbow) { $username = "{$username}"; } else { + $hasSetRainbow = true; $username = "{$username}"; } } diff --git a/public/announce.php b/public/announce.php index ea64bec5..0874708a 100644 --- a/public/announce.php +++ b/public/announce.php @@ -227,11 +227,13 @@ if ($compact == 1) { } //check ReAnnounce -$params = $_GET; -unset($params['key'], $params['ip'], $params['ipv4'], $params['ipv6']); -$reAnnounceQuery = http_build_query($params); -$lockKey = md5($reAnnounceQuery); -$log .= ", [CHECK_RE_ANNOUNCE], reAnnounceQuery: $reAnnounceQuery, lockKey: $lockKey"; +$lockParams = []; +foreach(['info_hash', 'passkey', 'peer_id'] as $lockField) { + $lockParams[$lockField] = $_GET[$lockField]; +} +$lockString = http_build_query($lockParams); +$lockKey = md5($lockString); +$log .= ", [CHECK_RE_ANNOUNCE], lockString: $lockString, lockKey: $lockKey"; $redis = $Cache->getRedis(); if (!$redis->set($lockKey, TIMENOW, ['nx', 'ex' => 5])) { do_log("$log, [YES_RE_ANNOUNCE]"); diff --git a/public/userdetails.php b/public/userdetails.php index c423ea42..6f63c751 100644 --- a/public/userdetails.php +++ b/public/userdetails.php @@ -306,7 +306,7 @@ JS; \Nexus\Nexus::js($warnMedalJs, 'footer', false); } //User meta -$metas = $userRep->listMetas($CURUSER['id']); +$metas = $userRep->listMetas($user['id']); $props = []; $metaKey = \App\Models\UserMeta::META_KEY_CHANGE_USERNAME; if ($metas->has($metaKey)) { @@ -358,14 +358,14 @@ if ($metas->has($metaKey)) { $rainbowID = $metas->get($metaKey)->first(); if ($rainbowID->isValid()) { $props[] = sprintf( - '
[%s](%s: %s)
', - $rainbowID->metaKeyText, nexus_trans('label.deadline'), $rainbowID->deadline + '
[%s](%s)
', + $rainbowID->metaKeyText, $rainbowID->getDeadlineText() ); } } if (!empty($props)) { - tr_small($lang_userdetails['row_user_props'], sprintf('
%s
', implode(' | ', $props)), 1); + tr_small($lang_userdetails['row_user_props'], sprintf('
%s
', implode(' | ', $props)), 1); } tr_small($lang_userdetails['row_torrent_comment'], ($torrentcomments && ($user["id"] == $CURUSER["id"] || get_user_class() >= $viewhistory_class) ? "".$torrentcomments."" : $torrentcomments), 1); diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php index ac1cdf63..c6817d14 100644 --- a/resources/lang/en/admin.php +++ b/resources/lang/en/admin.php @@ -62,6 +62,10 @@ return [ 'confirm_btn' => 'Confirm', 'disable_download_privileges_btn' => 'Enable download', 'enable_download_privileges_btn' => 'Disable download', + 'grant_prop_btn' => 'Grant prop', + 'grant_prop_form_prop' => 'Select prop', + 'grant_prop_form_duration' => 'Duration', + 'grant_prop_form_duration_help' => 'Unit: days. If left blank, the user has it permanently. Note: There is no time limit for Name Change Card, ignore this value.' , ] ], 'exam_user' => [ diff --git a/resources/lang/en/label.php b/resources/lang/en/label.php index e6635555..b76effb9 100644 --- a/resources/lang/en/label.php +++ b/resources/lang/en/label.php @@ -27,6 +27,7 @@ return [ 'description' => 'Description', 'price' => 'Price', 'deadline' => 'Deadline', + 'permanent' => 'Permanent', 'operator' => 'Operator', 'setting' => [ 'nav_text' => 'Setting', diff --git a/resources/lang/en/user.php b/resources/lang/en/user.php index 44015974..fb558ba2 100644 --- a/resources/lang/en/user.php +++ b/resources/lang/en/user.php @@ -15,6 +15,7 @@ return [ 'downloaded' => 'Downloaded', 'invites' => 'Invites', 'attendance_card' => 'Attend card', + 'props' => 'Props', ], 'class_name' => [ \App\Models\User::CLASS_VIP => 'Vip', diff --git a/resources/lang/zh_CN/admin.php b/resources/lang/zh_CN/admin.php index 4674af29..ca97a356 100644 --- a/resources/lang/zh_CN/admin.php +++ b/resources/lang/zh_CN/admin.php @@ -62,6 +62,10 @@ return [ 'confirm_btn' => '确认', 'disable_download_privileges_btn' => '禁用下载权限', 'enable_download_privileges_btn' => '启用下载权限', + 'grant_prop_btn' => '授予道具', + 'grant_prop_form_prop' => '选择道具', + 'grant_prop_form_duration' => '有效时长', + 'grant_prop_form_duration_help' => '单位:天。如果留空,用户永久拥有。注:改名卡没有时间限制,忽略该值。', ] ], 'exam_user' => [ diff --git a/resources/lang/zh_CN/label.php b/resources/lang/zh_CN/label.php index 66e18ac3..066196ad 100644 --- a/resources/lang/zh_CN/label.php +++ b/resources/lang/zh_CN/label.php @@ -27,6 +27,7 @@ return [ 'description' => '描述', 'price' => '价格', 'deadline' => '截止时间', + 'permanent' => '永久有效', 'operator' => '操作者', 'setting' => [ 'nav_text' => '设置', diff --git a/resources/lang/zh_CN/user.php b/resources/lang/zh_CN/user.php index 1748a20c..c98f5927 100644 --- a/resources/lang/zh_CN/user.php +++ b/resources/lang/zh_CN/user.php @@ -15,6 +15,7 @@ return [ 'downloaded' => '下载量', 'invites' => '邀请', 'attendance_card' => '补签卡', + 'props' => '道具', ], 'class_names' => [ \App\Models\User::CLASS_VIP => '贵宾', diff --git a/resources/lang/zh_TW/admin.php b/resources/lang/zh_TW/admin.php index e0a7bf3d..95abb3dc 100644 --- a/resources/lang/zh_TW/admin.php +++ b/resources/lang/zh_TW/admin.php @@ -62,6 +62,10 @@ return [ 'confirm_btn' => '確認', 'disable_download_privileges_btn' => '禁用下載權限', 'enable_download_privileges_btn' => '啟用下載權限', + 'grant_prop_btn' => '授予道具', + 'grant_prop_form_prop' => '選擇道具', + 'grant_prop_form_duration' => '有效時長', + 'grant_prop_form_duration_help' => '單位:天。如果留空,用戶永久擁有。註:改名卡沒有時間限製,忽略該值。', ] ], 'exam_user' => [ diff --git a/resources/lang/zh_TW/label.php b/resources/lang/zh_TW/label.php index db2e3776..0abc3dc7 100644 --- a/resources/lang/zh_TW/label.php +++ b/resources/lang/zh_TW/label.php @@ -27,6 +27,7 @@ return [ 'description' => '描述', 'price' => '價格', 'deadline' => '截止時間', + 'permanent' => '永久有效', 'operator' => '操作者', 'setting' => [ 'nav_text' => '設置', diff --git a/resources/lang/zh_TW/user.php b/resources/lang/zh_TW/user.php index bda1fa34..73c15278 100644 --- a/resources/lang/zh_TW/user.php +++ b/resources/lang/zh_TW/user.php @@ -15,6 +15,7 @@ return [ 'downloaded' => '下載量', 'invites' => '邀請', 'attendance_card' => '補簽卡', + 'props' => '道具', ], 'class_name' => [ \App\Models\User::CLASS_VIP => '貴賓', diff --git a/resources/views/filament/resources/user/user-resource/pages/user-profile.blade.php b/resources/views/filament/resources/user/user-resource/pages/user-profile.blade.php index fed7a594..e1b3b996 100644 --- a/resources/views/filament/resources/user/user-resource/pages/user-profile.blade.php +++ b/resources/views/filament/resources/user/user-resource/pages/user-profile.blade.php @@ -9,7 +9,7 @@ {{__('label.user.username')}} - {{$record->username}} + {!! get_username($record->id, false, true, true, true) !!} @@ -42,6 +42,13 @@ {{$record->classText}} + @if($props) + + {{__('user.labels.props')}} +
{!! implode(' | ', $props) !!}
+ + + @endif {{__('label.user.invite_by')}} {{$record->inviter->username ?? ''}}