diff --git a/app/Console/Commands/Test.php b/app/Console/Commands/Test.php index b82cac61..94863f2e 100644 --- a/app/Console/Commands/Test.php +++ b/app/Console/Commands/Test.php @@ -86,14 +86,9 @@ class Test extends Command */ public function handle() { - add_filter('ttt', function ($d) { - $d[] = 100; - return $d; - }); - $a = []; - $a[] = '1'; - $a = apply_filter('ttt', $a); - dd($a); + $a = Carbon::parse('2022-08-06 23:08:03'); + $b = $a->clone()->addHours(1); + dd($a, $b); } diff --git a/app/Console/ExecuteCommandTrait.php b/app/Console/ExecuteCommandTrait.php new file mode 100644 index 00000000..73240d55 --- /dev/null +++ b/app/Console/ExecuteCommandTrait.php @@ -0,0 +1,16 @@ +info("Running $command ..."); + $result = exec($command, $output, $result_code); + do_log(sprintf('command: %s, result_code: %s, output: %s, result: %s', $command, $result_code, json_encode($output), $result)); + if ($result_code != 0) { + throw new \RuntimeException(json_encode($output)); + } + } +} diff --git a/app/Models/BonusLogs.php b/app/Models/BonusLogs.php index 96d5bb71..1152b046 100644 --- a/app/Models/BonusLogs.php +++ b/app/Models/BonusLogs.php @@ -24,6 +24,7 @@ class BonusLogs extends NexusModel const BUSINESS_TYPE_GIFT_TO_SOMEONE = 10; const BUSINESS_TYPE_NO_AD = 11; const BUSINESS_TYPE_GIFT_TO_LOW_SHARE_RATIO = 12; + const BUSINESS_TYPE_LUCKY_DRAW = 13; public static array $businessTypes = [ self::BUSINESS_TYPE_CANCEL_HIT_AND_RUN => ['text' => 'Cancel H&R'], @@ -38,6 +39,7 @@ class BonusLogs extends NexusModel self::BUSINESS_TYPE_GIFT_TO_SOMEONE => ['text' => 'Gift to someone'], self::BUSINESS_TYPE_NO_AD => ['text' => 'No ad'], self::BUSINESS_TYPE_GIFT_TO_LOW_SHARE_RATIO => ['text' => 'Gift to low share ratio'], + self::BUSINESS_TYPE_LUCKY_DRAW => ['text' => 'Lucky draw'], ]; public static function getBonusForCancelHitAndRun() diff --git a/app/Models/NexusModel.php b/app/Models/NexusModel.php index 5a9d24f3..7390e18d 100644 --- a/app/Models/NexusModel.php +++ b/app/Models/NexusModel.php @@ -23,7 +23,7 @@ class NexusModel extends Model */ protected function serializeDate(\DateTimeInterface $date) { - return $date->format($this->dateFormat ?: 'Y-m-d H:i'); + return $date->format($this->dateFormat ?: 'Y-m-d H:i:s'); } /** diff --git a/app/Models/User.php b/app/Models/User.php index da80cdfa..3ebd2550 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -135,7 +135,7 @@ class User extends Authenticatable implements FilamentUser, HasName */ protected function serializeDate(\DateTimeInterface $date): string { - return $date->format($this->dateFormat ?: 'Y-m-d H:i'); + return $date->format($this->dateFormat ?: 'Y-m-d H:i:s'); } /** @@ -146,7 +146,7 @@ class User extends Authenticatable implements FilamentUser, HasName protected $fillable = [ 'username', 'email', 'passhash', 'secret', 'stylesheet', 'editsecret', 'added', 'modcomment', 'enabled', 'status', 'leechwarn', 'leechwarnuntil', 'page', 'class', 'uploaded', 'downloaded', 'clientselect', 'showclienterror', 'last_home', - 'seedbonus', 'bonuscomment', 'downloadpos', 'vip_added', 'vip_until', 'title', + 'seedbonus', 'bonuscomment', 'downloadpos', 'vip_added', 'vip_until', 'title', 'invites', 'attendance_card', ]; /** @@ -439,6 +439,16 @@ class User extends Authenticatable implements FilamentUser, HasName return $this->hasMany(PollAnswer::class, 'userid'); } + public function metas() + { + return $this->hasMany(UserMeta::class, 'uid'); + } + + public function usernameChangeLogs() + { + return $this->hasMany(UsernameChangeLog::class, 'uid'); + } + public function getAvatarAttribute($value) { if ($value) { diff --git a/app/Models/UserMeta.php b/app/Models/UserMeta.php new file mode 100644 index 00000000..3f232d1d --- /dev/null +++ b/app/Models/UserMeta.php @@ -0,0 +1,30 @@ +meta_key) ?? ''; + } + + public static function consumeBenefit($uid, $metaKey) + { + + } + +} diff --git a/app/Models/UsernameChangeLog.php b/app/Models/UsernameChangeLog.php new file mode 100644 index 00000000..7b8004aa --- /dev/null +++ b/app/Models/UsernameChangeLog.php @@ -0,0 +1,16 @@ +belongsTo(User::class, 'uid'); + } + +} diff --git a/app/Repositories/SeedBoxRepository.php b/app/Repositories/SeedBoxRepository.php index ba7f6e5b..ead82f04 100644 --- a/app/Repositories/SeedBoxRepository.php +++ b/app/Repositories/SeedBoxRepository.php @@ -73,7 +73,7 @@ class SeedBoxRepository extends BaseRepository } $params['version'] = $ipEnd->getVersion(); } else { - throw new \InvalidArgumentException("Require ip or ip_begin + ip_end"); + throw new \InvalidArgumentException(nexus_trans('label.seed_box_record.ip_help')); } return $params; diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index e15357aa..3fa3e203 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -10,8 +10,10 @@ use App\Models\Message; use App\Models\Setting; use App\Models\User; use App\Models\UserBanLog; +use App\Models\UserMeta; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Nexus\Database\NexusDB; @@ -82,12 +84,12 @@ class UserRepository extends BaseRepository } $username = $params['username']; if (!validusername($username)) { - throw new \InvalidArgumentException("Innvalid username: $username"); + throw new \InvalidArgumentException("Invalid username: $username"); } $email = htmlspecialchars(trim($params['email'])); $email = safe_email($email); if (!check_email($email)) { - throw new \InvalidArgumentException("Innvalid email: $email"); + throw new \InvalidArgumentException("Invalid email: $email"); } if (User::query()->where('email', $email)->exists()) { throw new \InvalidArgumentException("The email address: $email is already in use"); @@ -96,7 +98,7 @@ class UserRepository extends BaseRepository throw new \InvalidArgumentException("The username: $username is already in use"); } if (mb_strlen($password) < 6 || mb_strlen($password) > 40) { - throw new \InvalidArgumentException("Innvalid password: $password, it should be more than 6 character and less than 40 character"); + throw new \InvalidArgumentException("Invalid password: $password, it should be more than 6 character and less than 40 character"); } $class = !empty($params['class']) ? intval($params['class']) : User::CLASS_USER; if (!isset(User::$classes[$class])) { @@ -365,10 +367,64 @@ class UserRepository extends BaseRepository private function clearCache(User $user) { - \Nexus\Database\NexusDB::cache_del("user_{$user->id}_content"); - \Nexus\Database\NexusDB::cache_del('user_passkey_'.$user->passkey.'_content'); + clear_user_cache($user->id, $user->passkey); + } + + public function listMetas($uid, $metaKeys = [], $valid = true) + { + $query = UserMeta::query()->where('uid', $uid); + if (!empty($metaKeys)) { + $query->whereIn('meta_key', Arr::wrap($metaKeys)); + } + if ($valid) { + $query->where('status', 0)->where(function (Builder $query) { + $query->whereNull('deadline')->orWhere('deadline', '>=', now()); + }); + } + return $query->get()->groupBy('meta_key'); + } + + public function consumeBenefit($uid, array $params): bool + { + $metaKey = $params['meta_key']; + $records = $this->listMetas($uid, $metaKey); + if (!$records->has($metaKey)) { + throw new \RuntimeException("User do not has this metaKey: $metaKey"); + } + /** @var UserMeta $meta */ + $meta = $records->get($metaKey)->first(); + $user = User::query()->findOrFail($uid, User::$commonFields); + if ($metaKey == UserMeta::META_KEY_CHANGE_USERNAME) { + NexusDB::transaction(function () use ($user, $meta, $params) { + $this->changeUsername($user, $params['username']); + $meta->delete(); + clear_user_cache($user->id, $user->passkey); + }); + return true; + } + + throw new \InvalidArgumentException("Invalid meta_key: $metaKey"); + } + + private function changeUsername(User $user, $newUsername): bool + { + if ($user->username == $newUsername) { + throw new \RuntimeException("New username can not be the same with current username !"); + } + if (!validusername($newUsername)) { + throw new \InvalidArgumentException("Invalid username, length must between 4 and 20 characters"); + } + if (User::query()->where('username', $newUsername)->where('id', '!=', $user->id)->exists()) { + throw new \RuntimeException("Username: $newUsername already exists !"); + } + NexusDB::transaction(function () use ($user, $newUsername) { + $oldUsername = $user->username; + $user->usernameChangeLogs()->create(['username_old' => $oldUsername, 'username_new' => $newUsername]); + $user->username = $newUsername; + $user->save(); + }); + return true; } - } diff --git a/database/migrations/2022_08_09_163552_create_user_metas_table.php b/database/migrations/2022_08_09_163552_create_user_metas_table.php new file mode 100644 index 00000000..773d6502 --- /dev/null +++ b/database/migrations/2022_08_09_163552_create_user_metas_table.php @@ -0,0 +1,36 @@ +id(); + $table->integer('uid')->index(); + $table->string('meta_key')->index(); + $table->integer('status')->default(0); + $table->dateTime('deadline')->nullable(); + $table->text('meta_value')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_metas'); + } +}; diff --git a/database/migrations/2022_08_09_235716_create_username_change_logs_table.php b/database/migrations/2022_08_09_235716_create_username_change_logs_table.php new file mode 100644 index 00000000..23836a98 --- /dev/null +++ b/database/migrations/2022_08_09_235716_create_username_change_logs_table.php @@ -0,0 +1,34 @@ +id(); + $table->integer('uid'); + $table->string('username_old'); + $table->string('username_new'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('username_change_logs'); + } +}; diff --git a/include/constants.php b/include/constants.php index 396c1bf1..de2b274b 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ find($id, $neededColumns); + $user = \App\Models\User::query()->with(['wearing_medals'])->find($id, $neededColumns); if ($user) { - return $user->toArray(); + $userRep = new \App\Repositories\UserRepository(); + $metas = $userRep->listMetas($id, \App\Models\UserMeta::META_KEY_PERSONALIZED_USERNAME); + $arr = $user->toArray(); + if ($metas->isNotEmpty()) { + $arr['__is_rainbow'] = 1; + } else { + $arr['__is_rainbow'] = 0; + } + return $arr; } return null; }); @@ -2907,6 +2915,7 @@ jQuery(document).ready(function(){ JS; print($js); + print(''); print(""); //echo replacePngTags(ob_get_clean()); @@ -3151,7 +3160,7 @@ function commenttable($rows, $type, $parent_id, $review = false) $uidArr = array_unique(array_column($rows, 'user')); $neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title'); - $userInfoArr = \App\Models\User::query()->with(['wearing_medals'])->find($uidArr, $neededColumns)->keyBy('id'); + $userInfoArr = \App\Models\User::query()->find($uidArr, $neededColumns)->keyBy('id'); foreach ($rows as $row) { @@ -3166,7 +3175,7 @@ function commenttable($rows, $type, $parent_id, $review = false) } } print("
#" . $row["id"] . "  ".$lang_functions['text_by'].""); - print(build_medal_image($userInfo->wearing_medals, 20) . get_username($row["user"],false,true,true,false,false,true)); + print(get_username($row["user"],false,true,true,false,false,true)); print("  ".$lang_functions['text_at']."".gettime($row["added"]). ($row["editedby"] && get_user_class() >= $commanage_class ? " - [".$lang_functions['text_view_original']."]" : "") . "\"Top\"  
"); $avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars(trim($userRow["avatar"])) : ""); @@ -3420,7 +3429,7 @@ $torrent_tooltip = []; foreach ($rows as $row) { $id = $row["id"]; - $sphighlight = get_torrent_bg_color($row['sp_state'], $row['pos_state']); + $sphighlight = get_torrent_bg_color($row['sp_state'], $row['pos_state'], $row); print("\n"); print(""); @@ -3696,19 +3705,44 @@ function get_username($id, $big = false, $link = true, $bold = true, $target = f $disabledpic = "disabled"; $style = "style='margin-left: 2pt'"; } - $pics = $arr["donor"] == "yes" && ($arr['donoruntil'] === null || $arr['donoruntil'] < '1970' || $arr['donoruntil'] >= date('Y-m-d H:i:s')) ? "\"Donor\"" : ""; + $pics = $arr["donor"] == "yes" && ($arr['donoruntil'] === null || $arr['donoruntil'] < '1970' || $arr['donoruntil'] >= date('Y-m-d H:i:s')) ? "\"Donor\"" : ""; if ($arr["enabled"] == "yes") - $pics .= ($arr["leechwarn"] == "yes" ? "\"Leechwarned\"" : "") . ($arr["warned"] == "yes" ? "\"Warned\"" : ""); + $pics .= ($arr["leechwarn"] == "yes" ? "\"Leechwarned\"" : "") . ($arr["warned"] == "yes" ? "\"Warned\"" : ""); else - $pics .= "\"Disabled\"\n"; + $pics .= "\"Disabled\"\n"; + + //Rainbow effect + $username = $arr['username']; + $rainbow = ""; + $hasSetRainbow = false; + if (isset($arr['__is_rainbow']) && $arr['__is_rainbow']) { + $rainbow = ' class="rainbow"'; + } + if ($underline) { + $hasSetRainbow = true; + $username = "{$username}"; + } + if ($bold) { + if ($hasSetRainbow) { + $username = "{$username}"; + } else { + $username = "{$username}"; + } + } +// $username = ($underline == true ? "" . $arr['username'] . "" : $arr['username']); +// $username = ($bold == true ? "" . $username . "" : $username); + + //medal + $medalHtml = ''; + foreach ($arr['wearing_medals'] as $medal) { + $medalHtml .= sprintf('', $medal['image_large'], $medal['name']); + } - $username = ($underline == true ? "" . $arr['username'] . "" : $arr['username']); - $username = ($bold == true ? "" . $username . "" : $username); $href = getSchemeAndHttpHost() . "/userdetails.php?id=$id"; $username = ($link == true ? "" . $username . "" : $username) . $pics . ($withtitle == true ? " (" . ($arr['title'] == "" ? get_user_class_name($arr['class'],false,true,true) : "".htmlspecialchars($arr['title'])) . ")" : ""); - $username = "" . ( $bracket == true ? "(" . $username . ")" : $username) . ""; + $username = "$medalHtml" . ( $bracket == true ? "(" . $username . ")" : $username) . ""; } else { @@ -3791,11 +3825,15 @@ function validusername($username) // The following characters are allowed in user names $allowedchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - - for ($i = 0; $i < strlen($username); ++$i) + $length = strlen($username); + for ($i = 0; $i < $length; ++$i) if (strpos($allowedchars, $username[$i]) === false) return false; + if ($length < 4 || $length > 20) { + return false; + } + return true; } @@ -4171,7 +4209,7 @@ function get_second_icon($row) //for CHDBits } } -function get_torrent_bg_color($promotion = 1, $posState = "") +function get_torrent_bg_color($promotion = 1, $posState = "", array $torrent = []) { global $CURUSER; $sphighlight = null; @@ -4214,7 +4252,7 @@ function get_torrent_bg_color($promotion = 1, $posState = "") $sphighlight = sprintf(' style="background-color: %s"', $torrentSettings['sticky_second_level_background_color']); } } - return (string)$sphighlight; + return apply_filter('torrent_background_color', (string)$sphighlight, $torrent); } function get_torrent_promotion_append($promotion = 1,$forcemode = "",$showtimeleft = false, $added = "", $promotionTimeType = 0, $promotionUntil = '', $ignoreGlobal = false){ @@ -5615,7 +5653,7 @@ function build_medal_image(\Illuminate\Support\Collection $medals, $maxHeight = $wrapBefore = '
'; $wrapAfter = '
'; foreach ($medals as $medal) { - $html = sprintf('
', $medal->image_large, $medal->name, $maxHeight); + $html = sprintf('
', $medal->image_large, $medal->name, $maxHeight); if ($withActions) { $checked = ''; if ($medal->pivot->status == \App\Models\UserMedal::STATUS_WEARING) { diff --git a/include/globalfunctions.php b/include/globalfunctions.php index 24676e42..2e9d7840 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -602,40 +602,7 @@ function format_datetime($datetime, $format = 'Y-m-d H:i') function nexus_trans($key, $replace = [], $locale = null) { - if (!IN_NEXUS) { - return trans($key, $replace, $locale); - } - static $translations; - if (!$locale) { - $lang = get_langfolder_cookie(); - $locale = \App\Http\Middleware\Locale::$languageMaps[$lang] ?? 'en'; - } - if (is_null($translations)) { - $langDir = ROOT_PATH . 'resources/lang/'; - $files = glob($langDir . '*/*'); - foreach ($files as $file) { - $values = require $file; - $setKey = substr($file, strlen($langDir)); - if (substr($setKey, -4) == '.php') { - $setKey = substr($setKey, 0, -4); - } - $setKey = str_replace('/', '.', $setKey); - arr_set($translations, $setKey, $values); - } - } - $getKey = $locale . "." . $key; - $result = arr_get($translations, $getKey); - if (empty($result) && $locale != 'en') { - do_log("original getKey: $getKey can not get any translations", 'error'); - $getKey = "en." . $key; - $result = arr_get($translations, $getKey); - } - if (!empty($replace)) { - $search = array_map(function ($value) {return ":$value";}, array_keys($replace)); - $result = str_replace($search, array_values($replace), $result); - } - do_log("key: $key, replace: " . nexus_json_encode($replace) . ", locale: $locale, getKey: $getKey, result: $result", 'debug'); - return $result; + return \Nexus\Nexus::trans($key, $replace, $locale); } function isRunningInConsole(): bool @@ -903,3 +870,11 @@ function getDataTraffic(array $torrent, array $queries, array $user, $peer, $sna do_log("$log, result: " . json_encode($result), 'info'); return $result; } + +function clear_user_cache($uid, $passkey = '') +{ + \Nexus\Database\NexusDB::cache_del("user_{$uid}_content"); + if ($passkey) { + \Nexus\Database\NexusDB::cache_del('user_passkey_'.$passkey.'_content'); + } +} diff --git a/lang/chs/lang_userdetails.php b/lang/chs/lang_userdetails.php index 6c6345b5..d27d81cd 100644 --- a/lang/chs/lang_userdetails.php +++ b/lang/chs/lang_userdetails.php @@ -152,5 +152,8 @@ $lang_userdetails = array 'text_donoruntil_note' => "时间格式为'年年年年-月月-日日 时时:分分:秒秒'。留空永久有效。", 'change_field_value_migrated' => '修改请到管理后台操作', 'sure_to_remove_leech_warn' => '确定要清除此吸血警告吗?', + 'row_user_props' => '道具', + 'meta_key_change_username_username' => '新用户名', + 'consume' => '使用', ); ?> diff --git a/nexus/Nexus.php b/nexus/Nexus.php index 0802ad4e..0cbf8cd5 100644 --- a/nexus/Nexus.php +++ b/nexus/Nexus.php @@ -25,6 +25,10 @@ final class Nexus private static array $appendFooters = []; + private static array $translationNamespaces = []; + + private static array $translations = []; + const PLATFORM_USER = 'user'; const PLATFORM_ADMIN = 'admin'; const PLATFORM_TRACKER = 'tracker'; @@ -266,6 +270,87 @@ final class Nexus return self::$appendFooters; } + public static function addTranslationNamespace($path, $namespace) + { + if (empty($namespace)) { + throw new \InvalidArgumentException("namespace can not be empty"); + } + self::$translationNamespaces[$namespace] = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + } + + public static function trans($key, $replace = [], $locale = null) + { + if (!IN_NEXUS) { + return trans($key, $replace, $locale); + } + if (empty(self::$translations)) { + //load from default lang dir + $langDir = ROOT_PATH . 'resources/lang/'; + self::loadTranslations($langDir); + //load from namespace + foreach (self::$translationNamespaces as $namespace => $path) { + self::loadTranslations($path, $namespace); + } + } + return self::getTranslation($key, $replace, $locale); + } + + private static function loadTranslations($path, $namespace = null) + { + $files = glob($path . '*/*'); + foreach ($files as $file) { + if (!is_file($file)) { + continue; + } + if (!is_readable($file)) { + do_log("[TRANSLATION_FILE_NOT_READABLE], $file"); + } + $values = require $file; + $setKey = substr($file, strlen($path)); + if (substr($setKey, -4) == '.php') { + $setKey = substr($setKey, 0, -4); + } + $setKey = str_replace('/', '.', $setKey); + if ($namespace !== null) { + $setKey = "$namespace.$setKey"; + } +// do_log("path: $path, namespace: $namespace, file: $file, setKey: $setKey", 'debug'); + arr_set(self::$translations, $setKey, $values); + } + } + + private static function getTranslation($key, $replace = [], $locale = null) + { + if (!$locale) { + $lang = get_langfolder_cookie(); + $locale = \App\Http\Middleware\Locale::$languageMaps[$lang] ?? 'en'; + } + $getKey = self::getTranslationGetKey($key, $locale); + $result = arr_get(self::$translations, $getKey); + if (empty($result) && $locale != 'en') { + do_log("original getKey: $getKey can not get any translations", 'error'); + $getKey = self::getTranslationGetKey($key, 'en'); + $result = arr_get(self::$translations, $getKey); + } + if (!empty($replace)) { + $search = array_map(function ($value) {return ":$value";}, array_keys($replace)); + $result = str_replace($search, array_values($replace), $result); + } + do_log("key: $key, replace: " . nexus_json_encode($replace) . ", locale: $locale, getKey: $getKey, result: $result", 'debug'); + return $result; + } + + private static function getTranslationGetKey($key, $locale): string + { + $namespace = strstr($key, '::', true); + if ($namespace !== false) { + $getKey = sprintf('%s.%s.%s', $namespace, $locale, substr($key, strlen($namespace) + 2)); + } else { + $getKey = $locale . "." . $key; + } +// do_log("key: $key, locale: $locale, namespace: $namespace, getKey: $getKey", 'debug'); + return $getKey; + } } diff --git a/nexus/Plugin/Plugin.php b/nexus/Plugin/Plugin.php index e77ebf11..211396c1 100644 --- a/nexus/Plugin/Plugin.php +++ b/nexus/Plugin/Plugin.php @@ -36,8 +36,6 @@ class Plugin $className = str_replace('ServiceProvider', 'Repository', $provider); if (class_exists($className)) { call_user_func([new $className, 'boot']); - } else { - do_log("Plugin: $className not exists.", "error"); } } } diff --git a/public/ajax.php b/public/ajax.php index 9e611726..914f0ae6 100644 --- a/public/ajax.php +++ b/public/ajax.php @@ -116,3 +116,10 @@ function removeHitAndRun($params) $rep = new \App\Repositories\BonusRepository(); return $rep->consumeToCancelHitAndRun($CURUSER['id'], $params['id']); } + +function consumeBenefit($params) +{ + global $CURUSER; + $rep = new \App\Repositories\UserRepository(); + return $rep->consumeBenefit($CURUSER['id'], $params); +} diff --git a/public/details.php b/public/details.php index 11c0f8bc..528c2060 100644 --- a/public/details.php +++ b/public/details.php @@ -37,7 +37,7 @@ if (!$row) { permissiondenied(); } else { $row = apply_filter('torrent_detail', $row); - $owner = \App\Models\User::query()->with(['wearing_medals'])->find($row['owner']); + $owner = \App\Models\User::query()->find($row['owner']); if (!$owner) { $owner = \App\Models\User::defaultUser(); } @@ -104,10 +104,10 @@ if (!$row) { if (get_user_class() < $viewanonymous_class) $uprow = "".$lang_details['text_anonymous'].""; else - $uprow = "".$lang_details['text_anonymous']." (" . build_medal_image($owner->wearing_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) . ")"; + $uprow = "".$lang_details['text_anonymous']." (" . get_username($row['owner'], false, true, true, false, false, true) . ")"; } else { - $uprow = (isset($row['owner']) ? build_medal_image($owner->wearing_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) : "".$lang_details['text_unknown'].""); + $uprow = (isset($row['owner']) ? get_username($row['owner'], false, true, true, false, false, true) : "".$lang_details['text_unknown'].""); } if ($CURUSER["id"] == $row["owner"]) diff --git a/public/forums.php b/public/forums.php index 289ba616..9f370f38 100644 --- a/public/forums.php +++ b/public/forums.php @@ -667,7 +667,7 @@ if ($action == "viewtopic") $uidArr = array_keys($uidArr); unset($arr); $neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title'); - $userInfoArr = \App\Models\User::query()->with(['wearing_medals'])->find($uidArr, $neededColumns)->keyBy('id'); + $userInfoArr = \App\Models\User::query()->find($uidArr, $neededColumns)->keyBy('id'); $pn = 0; $lpr = get_last_read_post_id($topicid); if ($Advertisement->enable_ad()) @@ -708,7 +708,7 @@ if ($action == "viewtopic") $avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars($arr2["avatar"]) : ""); $uclass = get_user_class_image($arr2["class"]); - $by = build_medal_image($userInfo->wearing_medals, 20) . get_username($posterid,false,true,true,false,false,true); + $by = get_username($posterid,false,true,true,false,false,true); if (!$avatar) $avatar = "pic/default_avatar.png"; diff --git a/public/index.php b/public/index.php index 0156c1a2..7d9388cd 100644 --- a/public/index.php +++ b/public/index.php @@ -152,6 +152,11 @@ if ($showshoutbox_main == "yes") { print(""); } // ------------- end: shoutbox ------------------// + +$extraModules = []; +$extraModules = apply_filter('nexus_home_module', $extraModules); +print implode('', $extraModules); + // ------------- start: latest forum posts ------------------// if ($showlastxforumposts_main == "yes" && $CURUSER) diff --git a/public/js/jquery-easing.js b/public/js/jquery-easing.js new file mode 100644 index 00000000..0b24de00 --- /dev/null +++ b/public/js/jquery-easing.js @@ -0,0 +1,205 @@ +/* + * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ + * + * Uses the built in easing capabilities added In jQuery 1.1 + * to offer multiple easing options + * + * TERMS OF USE - jQuery Easing + * + * Open source under the BSD License. + * + * Copyright © 2008 George McGinley Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ + +// t: current time, b: begInnIng value, c: change In value, d: duration +jQuery.easing['jswing'] = jQuery.easing['swing']; + +jQuery.extend( jQuery.easing, + { + def: 'easeOutQuad', + swing: function (x, t, b, c, d) { + //alert(jQuery.easing.default); + return jQuery.easing[jQuery.easing.def](x, t, b, c, d); + }, + easeInQuad: function (x, t, b, c, d) { + return c*(t/=d)*t + b; + }, + easeOutQuad: function (x, t, b, c, d) { + return -c *(t/=d)*(t-2) + b; + }, + easeInOutQuad: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t + b; + return -c/2 * ((--t)*(t-2) - 1) + b; + }, + easeInCubic: function (x, t, b, c, d) { + return c*(t/=d)*t*t + b; + }, + easeOutCubic: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t + 1) + b; + }, + easeInOutCubic: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t + b; + return c/2*((t-=2)*t*t + 2) + b; + }, + easeInQuart: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t + b; + }, + easeOutQuart: function (x, t, b, c, d) { + return -c * ((t=t/d-1)*t*t*t - 1) + b; + }, + easeInOutQuart: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t + b; + return -c/2 * ((t-=2)*t*t*t - 2) + b; + }, + easeInQuint: function (x, t, b, c, d) { + return c*(t/=d)*t*t*t*t + b; + }, + easeOutQuint: function (x, t, b, c, d) { + return c*((t=t/d-1)*t*t*t*t + 1) + b; + }, + easeInOutQuint: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; + return c/2*((t-=2)*t*t*t*t + 2) + b; + }, + easeInSine: function (x, t, b, c, d) { + return -c * Math.cos(t/d * (Math.PI/2)) + c + b; + }, + easeOutSine: function (x, t, b, c, d) { + return c * Math.sin(t/d * (Math.PI/2)) + b; + }, + easeInOutSine: function (x, t, b, c, d) { + return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; + }, + easeInExpo: function (x, t, b, c, d) { + return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; + }, + easeOutExpo: function (x, t, b, c, d) { + return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; + }, + easeInOutExpo: function (x, t, b, c, d) { + if (t==0) return b; + if (t==d) return b+c; + if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; + return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; + }, + easeInCirc: function (x, t, b, c, d) { + return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; + }, + easeOutCirc: function (x, t, b, c, d) { + return c * Math.sqrt(1 - (t=t/d-1)*t) + b; + }, + easeInOutCirc: function (x, t, b, c, d) { + if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; + return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; + }, + easeInElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + }, + easeOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; + }, + easeInOutElastic: function (x, t, b, c, d) { + var s=1.70158;var p=0;var a=c; + if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; + }, + easeInBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*(t/=d)*t*((s+1)*t - s) + b; + }, + easeOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; + }, + easeInOutBack: function (x, t, b, c, d, s) { + if (s == undefined) s = 1.70158; + if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; + return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; + }, + easeInBounce: function (x, t, b, c, d) { + return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; + }, + easeOutBounce: function (x, t, b, c, d) { + if ((t/=d) < (1/2.75)) { + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)) { + return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; + } else if (t < (2.5/2.75)) { + return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; + } else { + return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; + } + }, + easeInOutBounce: function (x, t, b, c, d) { + if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; + return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; + } + }); + +/* + * + * TERMS OF USE - EASING EQUATIONS + * + * Open source under the BSD License. + * + * Copyright © 2001 Robert Penner + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the author nor the names of contributors may be used to endorse + * or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ diff --git a/public/js/jquery-rotate.js b/public/js/jquery-rotate.js new file mode 100644 index 00000000..f8540b23 --- /dev/null +++ b/public/js/jquery-rotate.js @@ -0,0 +1,339 @@ +// VERSION: 2.3 LAST UPDATE: 11.07.2013 +/* + * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php + * + * Made by Wilq32, wilq32@gmail.com, Wroclaw, Poland, 01.2009 + * Website: http://jqueryrotate.com + */ + +(function($) { + var supportedCSS,supportedCSSOrigin, styles=document.getElementsByTagName("head")[0].style,toCheck="transformProperty WebkitTransform OTransform msTransform MozTransform".split(" "); + for (var a = 0; a < toCheck.length; a++) if (styles[toCheck[a]] !== undefined) { supportedCSS = toCheck[a]; } + if (supportedCSS) { + supportedCSSOrigin = supportedCSS.replace(/[tT]ransform/,"TransformOrigin"); + if (supportedCSSOrigin[0] == "T") supportedCSSOrigin[0] = "t"; + } + + // Bad eval to preven google closure to remove it from code o_O + eval('IE = "v"=="\v"'); + + jQuery.fn.extend({ + rotate:function(parameters) + { + if (this.length===0||typeof parameters=="undefined") return; + if (typeof parameters=="number") parameters={angle:parameters}; + var returned=[]; + for (var i=0,i0=this.length;i this._parameters.duration; + + // TODO: Bug for animatedGif for static rotation ? (to test) + if (checkEnd && !this._parameters.animatedGif) + { + clearTimeout(this._timer); + } + else + { + if (this._canvas||this._vimage||this._img) { + var angle = this._parameters.easing(0, actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateTo - this._animateStartAngle, this._parameters.duration); + this._rotate((~~(angle*10))/10); + } + if (this._parameters.step) { + this._parameters.step(this._angle); + } + var self = this; + this._timer = setTimeout(function() + { + self._animate.call(self); + }, 10); + } + + // To fix Bug that prevents using recursive function in callback I moved this function to back + if (this._parameters.callback && checkEnd){ + this._angle = this._parameters.animateTo; + this._rotate(this._angle); + this._parameters.callback.call(this._rootObj); + } + }, + + _rotate : (function() + { + var rad = Math.PI/180; + if (IE) + return function(angle) + { + this._angle = angle; + this._container.style.rotation=(angle%360)+"deg"; + this._vimage.style.top = -(this._rotationCenterY - this._imgHeight/2) + "px"; + this._vimage.style.left = -(this._rotationCenterX - this._imgWidth/2) + "px"; + this._container.style.top = this._rotationCenterY - this._imgHeight/2 + "px"; + this._container.style.left = this._rotationCenterX - this._imgWidth/2 + "px"; + + } + else if (supportedCSS) + return function(angle){ + this._angle = angle; + this._img.style[supportedCSS]="rotate("+(angle%360)+"deg)"; + this._img.style[supportedCSSOrigin]=this._parameters.center.join(" "); + } + else + return function(angle) + { + this._angle = angle; + angle=(angle%360)* rad; + // clear canvas + this._canvas.width = this._width;//+this._widthAdd; + this._canvas.height = this._height;//+this._heightAdd; + + // REMEMBER: all drawings are read from backwards.. so first function is translate, then rotate, then translate, translate.. + this._cnv.translate(this._imgWidth*this._aspectW,this._imgHeight*this._aspectH); // at least center image on screen + this._cnv.translate(this._rotationCenterX,this._rotationCenterY); // we move image back to its orginal + this._cnv.rotate(angle); // rotate image + this._cnv.translate(-this._rotationCenterX,-this._rotationCenterY); // move image to its center, so we can rotate around its center + this._cnv.scale(this._aspectW,this._aspectH); // SCALE - if needed ;) + this._cnv.drawImage(this._img, 0, 0); // First - we draw image + } + + })() + } + + if (IE) + { + Wilq32.PhotoEffect.prototype.createVMLNode=(function(){ + document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)"); + try { + !document.namespaces.rvml && document.namespaces.add("rvml", "urn:schemas-microsoft-com:vml"); + return function (tagName) { + return document.createElement(''); + }; + } catch (e) { + return function (tagName) { + return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">'); + }; + } + })(); + } + +})(jQuery); diff --git a/public/js/nexus.js b/public/js/nexus.js index a8d4dcd3..3ace044b 100644 --- a/public/js/nexus.js +++ b/public/js/nexus.js @@ -1,10 +1,22 @@ -; -jQuery('.spoiler-title').on('click', function () { - let content = jQuery(this).parent().next(); - if (content.hasClass('collapse')) { - content.height(content[0].scrollHeight).removeClass('collapse') - } else { - content.height(0).addClass('collapse') - } -}); +jQuery(document).ready(function () { + jQuery('.spoiler-title').on('click', function () { + let content = jQuery(this).parent().next(); + if (content.hasClass('collapse')) { + content.height(content[0].scrollHeight).removeClass('collapse') + } else { + content.height(0).addClass('collapse') + } + }) + var previewEle = jQuery('#nexus-preview') + jQuery("body").on("mouseover", ".preview", function (e) { + console.log(e) + let src = jQuery(this).attr("src") + if (src) { + previewEle.attr("src", src).css({"display": "block", "left": e.pageX + 5, "top": e.pageY + 5}) + } + }); + jQuery("body").on("mouseout", ".preview", function (e) { + previewEle.hide() + }); +}) diff --git a/public/staffmess.php b/public/staffmess.php index 17d1f765..699c31a2 100644 --- a/public/staffmess.php +++ b/public/staffmess.php @@ -5,6 +5,7 @@ loggedinorreturn(); if (get_user_class() < UC_ADMINISTRATOR) stderr("Sorry", "Access denied."); stdhead("Mass PM", false); +$classes = array_chunk(\App\Models\User::$classes, 4, true); ?> diff --git a/public/styles/sprites.css b/public/styles/sprites.css index d809b11c..e4ee6cae 100644 --- a/public/styles/sprites.css +++ b/public/styles/sprites.css @@ -498,3 +498,35 @@ img.hitandrun { .layui-layer { color: black; } +.layer-form { + padding: 10px 15px; +} +.layer-form .form-control-row input{ + padding: 4px; +} +.layer-form .form-control-row .label{ + margin-bottom: 4px; +} +.rainbow { + text-align: center; + text-decoration: underline; + /*font-size: 32px;*/ + font-family: monospace; + /*letter-spacing: 5px;*/ + background: linear-gradient(to right, #6666ff, #0099ff , #00ff00, #ff3399, #6666ff); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + animation: rainbow_animation 6s ease-in-out infinite; + background-size: 400% 100%; +} + +@keyframes rainbow_animation { + 0%,100% { + background-position: 0 0; + } + + 50% { + background-position: 100% 0; + } +} diff --git a/public/takestaffmess.php b/public/takestaffmess.php index 2cb2ad69..86e0383d 100644 --- a/public/takestaffmess.php +++ b/public/takestaffmess.php @@ -3,7 +3,7 @@ require "../include/bittorrent.php"; if ($_SERVER["REQUEST_METHOD"] != "POST") stderr("Error", "Permission denied!"); dbconn(); -loggedinorreturn(); +loggedinorreturn(); if (get_user_class() < UC_ADMINISTRATOR) stderr("Sorry", "Permission denied."); @@ -25,10 +25,26 @@ if (is_array($updateset)) { stderr("Error","Invalid Class"); } $subject = trim($_POST['subject']); -$query = sql_query("SELECT id FROM users WHERE class IN (".implode(",", $updateset).")"); -while($dat=mysql_fetch_assoc($query)) -{ - sql_query("INSERT INTO messages (sender, receiver, added, subject, msg) VALUES ($sender_id, {$dat['id']}, $dt, " . sqlesc($subject) .", " . sqlesc($msg) .")") or sqlerr(__FILE__,__LINE__); +$size = 10000; +$page = 1; +set_time_limit(300); +$classStr = implode(",", $updateset); +while (true) { + $msgValues = $idArr = []; + $offset = ($page - 1) * $size; + $query = sql_query("SELECT id FROM users WHERE class IN (".implode(",", $updateset).") and `enabled` = 'yes' and `status` = 'confirmed'"); + while($dat=mysql_fetch_assoc($query)) + { + $idArr[] = $dat['id']; + $msgValues[] = sprintf('(%s, %s, %s, %s, %s)', $sender_id, $dat['id'], $dt, sqlesc($subject), sqlesc($msg)); + } + if (empty($idArr)) { + break; + } + $idStr = implode(', ', $idArr); + $sql = "INSERT INTO messages (sender, receiver, added, subject, msg) VALUES " . implode(', ', $msgValues); + sql_query($sql); + $page++; } header("Refresh: 0; url=staffmess.php?sent=1"); diff --git a/public/userdetails.php b/public/userdetails.php index 312e40f7..8dece202 100644 --- a/public/userdetails.php +++ b/public/userdetails.php @@ -29,6 +29,7 @@ if ($user["status"] == "pending") stderr($lang_userdetails['std_sorry'], $lang_userdetails['std_user_not_confirmed']); $userInfo = \App\Models\User::query()->with(['valid_medals'])->findOrFail($user['id']); +$userRep = new \App\Repositories\UserRepository(); if ($user['added'] == "0000-00-00 00:00:00" || $user['added'] == null) $joindate = $lang_userdetails['text_not_available']; @@ -279,6 +280,14 @@ tr_small($lang_userdetails['row_donated'], "$".htmlspecialchars($user['donated'] if ($user["avatar"]) tr_small($lang_userdetails['row_avatar'], return_avatar_image(htmlspecialchars(trim($user["avatar"]))), 1); +$uclass = get_user_class_image($user["class"]); +$utitle = get_user_class_name($user["class"],false,false,true); +$uclassImg = "\"".get_user_class_name($user["class"],false,false,true)."\" ".($user['title']!=="" ? " ".htmlspecialchars(trim($user["title"]))."" : ""); +if ($user['class'] == UC_VIP && !empty($user['vip_until']) && strtotime($user['vip_until'])) { + $uclassImg .= sprintf('%s: %s', $lang_userdetails['row_vip_until'], $user['vip_until']); +} +tr_small($lang_userdetails['row_class'], $uclassImg, 1); + if ($userInfo->valid_medals->isNotEmpty()) { tr_small($lang_userdetails['row_medal'], build_medal_image($userInfo->valid_medals, 200, $CURUSER['id'] == $user['id']), 1); $warnMedalJs = << ".($user['title']!=="" ? " ".htmlspecialchars(trim($user["title"]))."" : ""); -if ($user['class'] == UC_VIP && !empty($user['vip_until']) && strtotime($user['vip_until'])) { - $uclassImg .= sprintf('%s: %s', $lang_userdetails['row_vip_until'], $user['vip_until']); +//User meta +$metas = $userRep->listMetas($CURUSER['id']); +$props = []; +$metaKey = \App\Models\UserMeta::META_KEY_CHANGE_USERNAME; +if ($metas->has($metaKey)) { + $triggerId = "consume-$metaKey"; + $changeUsernameCards = $metas->get($metaKey); + $cardName = $changeUsernameCards->first()->meta_key_text; + $props[] = sprintf( + '
[%s](%s)
', + $cardName, $changeUsernameCards->count(), $lang_userdetails['consume'], $triggerId + ); + $consumeChangeUsernameForm = << +
+ +
+
{$lang_userdetails['meta_key_change_username_username']}
+
+
+ + +HTML; + $consumeChangeUsernameJs = <<%s', implode(' | ', $props)), 1); } -tr_small($lang_userdetails['row_class'], $uclassImg, 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/index.php b/resources/lang/en/index.php new file mode 100644 index 00000000..ca5d8ed5 --- /dev/null +++ b/resources/lang/en/index.php @@ -0,0 +1,5 @@ + 'IP(Block)', 'ip_begin' => 'Begin IP', 'ip_end' => 'End IP', - 'ip_help' => 'Begin IP/End IP, IP(Block) Choose one', + 'ip_help' => 'Fill in the begin IP + end IP, or IP (Block), not both', 'status' => 'Status', ], 'menu' => [ @@ -208,4 +208,10 @@ return [ 'parent_id' => 'Parent', 'min_class' => 'Minimum visible class', ], + 'user_meta' => [ + 'meta_keys' => [ + \App\Models\UserMeta::META_KEY_CHANGE_USERNAME => 'Name Change Card', + \App\Models\UserMeta::META_KEY_PERSONALIZED_USERNAME => 'Rainbow ID', + ], + ], ]; diff --git a/resources/lang/zh_CN/index.php b/resources/lang/zh_CN/index.php new file mode 100644 index 00000000..ca5d8ed5 --- /dev/null +++ b/resources/lang/zh_CN/index.php @@ -0,0 +1,5 @@ + 'IP(段)', 'ip_begin' => '起始 IP', 'ip_end' => '结束 IP', - 'ip_help' => '起始 IP/结束 IP、IP(段) 二选一', + 'ip_help' => '填写起始 IP + 结束 IP,或 IP(段),不要同时填写', 'status' => '状态', ], 'menu' => [ @@ -211,4 +211,10 @@ return [ 'parent_id' => '父菜单', 'min_class' => '最低可见等级', ], + 'user_meta' => [ + 'meta_keys' => [ + \App\Models\UserMeta::META_KEY_CHANGE_USERNAME => '更名卡', + \App\Models\UserMeta::META_KEY_PERSONALIZED_USERNAME => '彩虹 ID', + ], + ], ]; diff --git a/resources/lang/zh_TW/index.php b/resources/lang/zh_TW/index.php new file mode 100644 index 00000000..ca5d8ed5 --- /dev/null +++ b/resources/lang/zh_TW/index.php @@ -0,0 +1,5 @@ + 'IP(段)', 'ip_begin' => '起始 IP', 'ip_end' => '結束 IP', - 'ip_help' => '起始 IP/結束 IP、IP(段) 二選一', + 'ip_help' => '填寫起始 IP + 結束 IP,或 IP(段),不要同時填寫', 'status' => '狀態', ], 'menu' => [ @@ -208,4 +208,10 @@ return [ 'parent_id' => '父菜單', 'min_class' => '最低可見等級', ], + 'user_meta' => [ + 'meta_keys' => [ + \App\Models\UserMeta::META_KEY_CHANGE_USERNAME => '更名卡', + \App\Models\UserMeta::META_KEY_PERSONALIZED_USERNAME => '彩虹 ID', + ], + ], ];
@@ -30,79 +31,15 @@ if ($_GET["sent"] == 1) {
Send to:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + '); + foreach ($chunk as $class => $info) { + printf('', $class, $info['text']); + } + printf(''); + } + ?>
- Peasant - User - Power User - Elite User
- Crazy User - Insane User - Veteran User - Extreme User
- Ultimate User - Nexus Master - VIP - Uploader
- Moderator - Administrator - SysOp - Staff Leader