diff --git a/db/migrations/20260523222340_add_time_zone_user_setting.php b/db/migrations/20260523222340_add_time_zone_user_setting.php new file mode 100644 index 0000000..5b32758 --- /dev/null +++ b/db/migrations/20260523222340_add_time_zone_user_setting.php @@ -0,0 +1,26 @@ +table('sys_users'); + $table->addColumn('timezone', 'string', ['limit' => 255, 'null' => false, 'default' => '+0900']) + ->update(); + } +} diff --git a/web/cgb/mario_kart.php b/web/cgb/mario_kart.php index 333d765..f7fa35b 100644 --- a/web/cgb/mario_kart.php +++ b/web/cgb/mario_kart.php @@ -1,6 +1,7 @@ prepare("select dion_email_local from sys_users where id = ?"); - $stmt->bind_param("i", $user_id); + $stmt = $db->prepare("select id from sys_users where dion_email_local = ?"); + $stmt->bind_param("s", $decoded["email_id"]); $stmt->execute(); $result = fancy_get_result($stmt); if (sizeof($result) == 0) { return false; } - - if ($result[0]["dion_email_local"] !== $decoded["email_id"]) { + + if (is_null($user_id)) { + return $result[0]["id"]; + } elseif ($result[0]["id"] !== $user_id) { return false; + } else { + return true; } - - return true; } function getCurrentMobileGP() { @@ -279,11 +282,12 @@ function getOwnRankMobileGP($gp_id, $myid) { } function makeGhostDownload($result, $course) { - $output = pack("n", date("Y", time() + 32400)) // Year - .pack("C", date("m", time() + 32400)) // Month - .pack("C", date("d", time() + 32400)) // Day - .pack("C", date("H", time() + 32400)) // Hour - .pack("C", date("i")); // Minute + $time = get_user_local_time(); + $output = pack("n", $time->format("Y")) // Year + .pack("C", $time->format("m")) // Month + .pack("C", $time->format("d")) // Day + .pack("C", $time->format("H")) // Hour + .pack("C", $time->format("i")); // Minute $total = getTotalRankingEntries($course); @@ -394,11 +398,12 @@ function getPlayerIDByRank($course) { // 0.dlghostid.cgb (unused) $stmt->execute(); $result = fancy_get_result($stmt); - $output = pack("n", date("Y", time() + 32400)) // Year - .pack("C", date("m", time() + 32400)) // Month - .pack("C", date("d", time() + 32400)) // Day - .pack("C", date("H", time() + 32400)) // Hour - .pack("C", date("i")); // Minute + $time = get_user_local_time(); + $output = pack("n", $time->format("Y")) // Year + .pack("C", $time->format("m")) // Month + .pack("C", $time->format("d")) // Day + .pack("C", $time->format("H")) // Hour + .pack("C", $time->format("i")); // Minute $output = $output.pack("N", getTotalRankingEntries($course)); // Seems to indicate if something was found $output = $output.pack("n", sizeof($result)); // Seems to indicate if data is present @@ -431,11 +436,12 @@ function getPlayerIDByStateRank($course) { // 0.dlghostst.cgb $stmt->execute(); $result = fancy_get_result($stmt); - $output = pack("n", date("Y", time() + 32400)) // Year - .pack("C", date("m", time() + 32400)) // Month - .pack("C", date("d", time() + 32400)) // Day - .pack("C", date("H", time() + 32400)) // Hour - .pack("C", date("i")); // Minute + $time = get_user_local_time(); + $output = pack("n", $time->format("Y")) // Year + .pack("C", $time->format("m")) // Month + .pack("C", $time->format("d")) // Day + .pack("C", $time->format("H")) // Hour + .pack("C", $time->format("i")); // Minute $output = $output.pack("N", getTotalRankingEntriesState($course, $state)); // Seems to indicate if something was found $output = $output.pack("n", sizeof($result)); // Seems to indicate if data is present @@ -468,11 +474,12 @@ function getPlayerIDByDriverRank($course) { // 0.dlghostdr.cgb $stmt->execute(); $result = fancy_get_result($stmt); - $output = pack("n", date("Y", time() + 32400)) // Year - .pack("C", date("m", time() + 32400)) // Month - .pack("C", date("d", time() + 32400)) // Day - .pack("C", date("H", time() + 32400)) // Hour - .pack("C", date("i")); // Minute + $time = get_user_local_time(); + $output = pack("n", $time->format("Y")) // Year + .pack("C", $time->format("m")) // Month + .pack("C", $time->format("d")) // Day + .pack("C", $time->format("H")) // Hour + .pack("C", $time->format("i")); // Minute $output = $output.pack("N", getTotalRankingEntriesDriver($course, $driver)); // Seems to indicate if something was found $output = $output.pack("n", sizeof($result)); // Seems to indicate if data is present @@ -595,10 +602,11 @@ function query($course) { // query.cgb return; } $myid = hex2bin($params["myid"]); - /*if (!checkPlayerID($myid, null)) { + $user_id = checkPlayerID($myid); + if ($user_id === false) { http_response_code(400); return; - }*/ + } $state = hexdec($params["state"]); $driver = hexdec($params["driver"]); $rk = array(); @@ -606,11 +614,12 @@ function query($course) { // query.cgb array_push($rk, hexdec($params["rk_".$i])); } - echo pack("n", date("Y", time() + 32400)); // Year - echo pack("C", date("m", time() + 32400)); // Month - echo pack("C", date("d", time() + 32400)); // Day - echo pack("C", date("H", time() + 32400)); // Hour - echo pack("C", date("i")); // Minute + $time = get_user_local_time($user_id); + echo pack("n", $time->format("Y")); // Year + echo pack("C", $time->format("m")); // Month + echo pack("C", $time->format("d")); // Day + echo pack("C", $time->format("H")); // Hour + echo pack("C", $time->format("i")); // Minute echo pack("N", getTotalRankingEntries($course)); // Probably total amount of ranked players @@ -656,11 +665,11 @@ function query($course) { // query.cgb // --- State top 10 --- // - echo pack("n", date("Y", time() + 32400)); // Year - echo pack("C", date("m", time() + 32400)); // Month - echo pack("C", date("d", time() + 32400)); // Day - echo pack("C", date("H", time() + 32400)); // Hour - echo pack("C", date("i")); // Minute + echo pack("n", $time->format("Y")); // Year + echo pack("C", $time->format("m")); // Month + echo pack("C", $time->format("d")); // Day + echo pack("C", $time->format("H")); // Hour + echo pack("C", $time->format("i")); // Minute echo pack("N", getTotalRankingEntriesState($course, $state)); // Probably total amount of ranked players @@ -690,11 +699,11 @@ function query($course) { // query.cgb // --- Driver top 10 --- // - echo pack("n", date("Y", time() + 32400)); // Year - echo pack("C", date("m", time() + 32400)); // Month - echo pack("C", date("d", time() + 32400)); // Day - echo pack("C", date("H", time() + 32400)); // Hour - echo pack("C", date("i")); // Minute + echo pack("n", $time->format("Y")); // Year + echo pack("C", $time->format("m")); // Month + echo pack("C", $time->format("d")); // Day + echo pack("C", $time->format("H")); // Hour + echo pack("C", $time->format("i")); // Minute echo pack("N", getTotalRankingEntriesDriver($course, $driver)); // Probably total amount of ranked players diff --git a/web/cgb/ranking/01/AGB-AMKJ/mobilegp/query.php b/web/cgb/ranking/01/AGB-AMKJ/mobilegp/query.php index 191741e..ace91de 100644 --- a/web/cgb/ranking/01/AGB-AMKJ/mobilegp/query.php +++ b/web/cgb/ranking/01/AGB-AMKJ/mobilegp/query.php @@ -13,7 +13,8 @@ return; } $myid = hex2bin($params["myid"]); - if (!checkPlayerID($myid, null)) { + $user_id = checkPlayerID($myid); + if ($user_id === false) { http_response_code(400); return; } @@ -22,11 +23,12 @@ $gp_id = getCurrentMobileGP(); - echo pack("n", date("Y", time() + 32400)); // Year - echo pack("C", date("m", time() + 32400)); // Month - echo pack("C", date("d", time() + 32400)); // Day - echo pack("C", date("H", time() + 32400)); // Hour - echo pack("C", date("i")); // Minute + $time = get_user_local_time($user_id); + echo pack("n", $time->format("Y")); // Year + echo pack("C", $time->format("m")); // Month + echo pack("C", $time->format("d")); // Day + echo pack("C", $time->format("H")); // Hour + echo pack("C", $time->format("i")); // Minute echo pack("N", getTotalRankingEntriesMobileGP($gp_id)); diff --git a/web/cgb/ranking/A4/AGB-AMGJ/0.query.php b/web/cgb/ranking/A4/AGB-AMGJ/0.query.php index 8aa44dc..2467d83 100644 --- a/web/cgb/ranking/A4/AGB-AMGJ/0.query.php +++ b/web/cgb/ranking/A4/AGB-AMGJ/0.query.php @@ -1,6 +1,7 @@ prepare("select count(*) from amg_rankings"); + $stmt = $db->prepare("select count(*) from amg_rankings"); $stmt->execute(); $result = fancy_get_result($stmt); $count = $result[0]["count(*)"]; @@ -35,11 +36,12 @@ } } - echo pack("n", date("Y", time() + 32400)); - echo pack("C", date("m", time() + 32400)); - echo pack("C", date("d", time() + 32400)); - echo pack("C", date("H", time() + 32400)); - echo pack("C", date("i")); + $time = get_user_local_time(); + echo pack("n", $time->format("Y")); // Year + echo pack("C", $time->format("m")); // Month + echo pack("C", $time->format("d")); // Day + echo pack("C", $time->format("H")); // Hour + echo pack("C", $time->format("i")); // Minute echo pack("n", sizeof($top10)); for ($i = 0; $i < sizeof($top10); $i++) { diff --git a/web/cgb/timezone.php b/web/cgb/timezone.php new file mode 100644 index 0000000..5976b3f --- /dev/null +++ b/web/cgb/timezone.php @@ -0,0 +1,22 @@ +prepare("select timezone from sys_users where id = ?"); + $stmt->bind_param("i", $user_id); + $stmt->execute(); + $result = fancy_get_result($stmt); + if (sizeof($result) != 0) { + $tz = $result[0]["timezone"]; + } + } + return date_create_immutable("now", timezone_open($tz)); + } +?> diff --git a/web/cgb/upload/A4/AGB-AMGJ/register.php b/web/cgb/upload/A4/AGB-AMGJ/register.php index e3c7252..d697d1c 100644 --- a/web/cgb/upload/A4/AGB-AMGJ/register.php +++ b/web/cgb/upload/A4/AGB-AMGJ/register.php @@ -26,11 +26,12 @@ http_response_code(500); return; } -$stmt = $db->prepare("select count(*) from sys_users where dion_email_local = ?"); + + $stmt = $db->prepare("select id from sys_users where dion_email_local = ?"); $stmt->bind_param("s", $dion_email_local); $stmt->execute(); $result = fancy_get_result($stmt); - if ($result[0]["count(*)"] == 0) { + if (sizeof($result) == 0) { http_response_code(400); return; } @@ -41,9 +42,10 @@ return; } - echo pack("n", date("Y", time() + 32400)); - echo pack("C", date("m", time() + 32400)); - echo pack("C", date("d", time() + 32400)); + $time = get_user_local_time($result[0]["id"]); + echo pack("n", $time->format("Y")); // Year + echo pack("C", $time->format("m")); // Month + echo pack("C", $time->format("d")); // Day $stmt = $db->prepare("select id, weight from amg_rankings where ident = ? and game_region = ?"); $stmt->bind_param("ss", $ident, $game_region); diff --git a/web/htdocs/user/summary.php b/web/htdocs/user/summary.php index 33ed538..b7e6271 100644 --- a/web/htdocs/user/summary.php +++ b/web/htdocs/user/summary.php @@ -32,11 +32,21 @@ $errors[] = "pokemonNewsValue"; } } + if (array_key_exists("timeZone", $_POST)) { + if ($_POST["timeZone"] === "+0900" || in_array($_POST["timeZone"], timezone_identifiers_list(), true)) { + $db = DBUtil::getInstance()->getDB(); + $stmt = $db->prepare("update sys_users set timezone = ? where id = ?"); + $stmt->bind_param("si", $_POST["timeZone"], $_SESSION["user_id"]); + $stmt->execute(); + } else { + $errors[] = "timeZoneValue"; + } + } $db = $db_util->getDB(); - $stmt = $db->prepare("select email, dion_ppp_id, dion_email_local, log_in_password, money_spent, trade_region_allowlist, custom_pokemon_news_opt_in from sys_users where id = ?"); + $stmt = $db->prepare("select email, dion_ppp_id, dion_email_local, log_in_password, money_spent, trade_region_allowlist, custom_pokemon_news_opt_in, timezone from sys_users where id = ?"); $stmt->bind_param("i", $_SESSION["user_id"]); $stmt->execute(); $result = DBUtil::fancy_get_result($stmt)[0]; @@ -55,6 +65,8 @@ "money_spent" => $result["money_spent"], "trade_region_allowlist" => $result["trade_region_allowlist"], "pokemon_news_custom_opt_in" => intval($result["custom_pokemon_news_opt_in"]), + "time_zone" => $result["timezone"], + "all_time_zones" => timezone_identifiers_list(), "inbox_size" => $inbox_size, "errors" => $errors ]); diff --git a/web/locales/en.yml b/web/locales/en.yml index 548e330..52c2369 100644 --- a/web/locales/en.yml +++ b/web/locales/en.yml @@ -44,6 +44,8 @@ wrong-password: Wrong password link-invalid-expired: This link is invalid or has expired. invalid-trade-setting: Not a valid region pool setting. invalid-pokemon-news-setting: Not a valid Pokémon news preference. +invalid-time-zone-setting: Not a valid time zone setting. +default-time-zone: UTC+9 (default) auth: login-to-reon: Login to your REON account invalid-email-pass: Incorrect email or password @@ -92,7 +94,7 @@ account: email-changed-success: Your email was changed successfully. change-your-password: Change your password password-changed-success: Your password was changed successfully. - pokemon-crystal-settings: Pokémon Crystal settings + pokemon-crystal-settings: Other settings trade-corner-region-allowlist: 'Trade Corner: Trading Preferences' trade-region-allowlist-blurb: Select the region pool you’re willing to trade with when you deposit your Pokémon in the Trade Corner. Any match you receive will @@ -109,6 +111,9 @@ account: If no custom news exists, official news will be used instead. pokemon-news-custom-opt-in-off: Official Pokémon News only pokemon-news-custom-opt-in-on: Custom Pokémon News when available + time-zone: Time zone preference + time-zone-blurb: The time zone to use for in-game timestamps. + Note that UTC+9 timestamps may appear depending on the game and context. index: what-is-reon: What is REON? reon-description: REON is something. diff --git a/web/locales/fr.yml b/web/locales/fr.yml index bc71e34..0b68cb5 100644 --- a/web/locales/fr.yml +++ b/web/locales/fr.yml @@ -45,6 +45,8 @@ wrong-password: Mot de passe incorrect link-invalid-expired: Soit ce lien est invalide, soit il a expiré. invalid-trade-setting: Réglage invalide pour le pool des régions. invalid-pokemon-news-setting: Préférence d’actualités Pokémon invalide. +invalid-time-zone: Fuseau horaire invalide. +default-time-zone: UTC+9 (défaut) auth: login-to-reon: Se connecter à votre compte REON invalid-email-pass: Addresse ou mot de passe incorrect @@ -83,7 +85,7 @@ account: email-changed-success: Votre addresse courriel a été changé avec succès. change-your-password: Changer votre mot de passe password-changed-success: Votre mot de passe a été changé avec succès. - pokemon-crystal-settings: Paramètres Pokémon Cristal + pokemon-crystal-settings: Autres paramètres trade-corner-region-allowlist: 'Coin Troc : Préférences d''Échange International' trade-region-allowlist-blurb: Selectionnez les régions avec qui vous voulez échanger au Coin Troc. N'import quel Pokémon que vous recevez sera d'en un. « Régions avec @@ -100,6 +102,9 @@ account: officielles seront utilisées à la place. pokemon-news-custom-opt-in-off: Actualités Pokémon officielles uniquement pokemon-news-custom-opt-in-on: Actualités Pokémon personnalisées lorsqu’elles sont disponibles + time-zone: Préférence de fuseau horaire + time-zone-blurb: Le fuseau horaire à utiliser pour les horodatages dans les jeux. + Notez bien que quelques horodatages peut s'afficher en UTC+9, en fonction du jeu et du contexte. index: what-is-reon: Qu'est-ce c'est REON? reon-description: REON est quelque chose. diff --git a/web/locales/locales/en.yml b/web/locales/locales/en.yml index 548e330..52c2369 100644 --- a/web/locales/locales/en.yml +++ b/web/locales/locales/en.yml @@ -44,6 +44,8 @@ wrong-password: Wrong password link-invalid-expired: This link is invalid or has expired. invalid-trade-setting: Not a valid region pool setting. invalid-pokemon-news-setting: Not a valid Pokémon news preference. +invalid-time-zone-setting: Not a valid time zone setting. +default-time-zone: UTC+9 (default) auth: login-to-reon: Login to your REON account invalid-email-pass: Incorrect email or password @@ -92,7 +94,7 @@ account: email-changed-success: Your email was changed successfully. change-your-password: Change your password password-changed-success: Your password was changed successfully. - pokemon-crystal-settings: Pokémon Crystal settings + pokemon-crystal-settings: Other settings trade-corner-region-allowlist: 'Trade Corner: Trading Preferences' trade-region-allowlist-blurb: Select the region pool you’re willing to trade with when you deposit your Pokémon in the Trade Corner. Any match you receive will @@ -109,6 +111,9 @@ account: If no custom news exists, official news will be used instead. pokemon-news-custom-opt-in-off: Official Pokémon News only pokemon-news-custom-opt-in-on: Custom Pokémon News when available + time-zone: Time zone preference + time-zone-blurb: The time zone to use for in-game timestamps. + Note that UTC+9 timestamps may appear depending on the game and context. index: what-is-reon: What is REON? reon-description: REON is something. diff --git a/web/locales/locales/fr.yml b/web/locales/locales/fr.yml index bc71e34..0b68cb5 100644 --- a/web/locales/locales/fr.yml +++ b/web/locales/locales/fr.yml @@ -45,6 +45,8 @@ wrong-password: Mot de passe incorrect link-invalid-expired: Soit ce lien est invalide, soit il a expiré. invalid-trade-setting: Réglage invalide pour le pool des régions. invalid-pokemon-news-setting: Préférence d’actualités Pokémon invalide. +invalid-time-zone: Fuseau horaire invalide. +default-time-zone: UTC+9 (défaut) auth: login-to-reon: Se connecter à votre compte REON invalid-email-pass: Addresse ou mot de passe incorrect @@ -83,7 +85,7 @@ account: email-changed-success: Votre addresse courriel a été changé avec succès. change-your-password: Changer votre mot de passe password-changed-success: Votre mot de passe a été changé avec succès. - pokemon-crystal-settings: Paramètres Pokémon Cristal + pokemon-crystal-settings: Autres paramètres trade-corner-region-allowlist: 'Coin Troc : Préférences d''Échange International' trade-region-allowlist-blurb: Selectionnez les régions avec qui vous voulez échanger au Coin Troc. N'import quel Pokémon que vous recevez sera d'en un. « Régions avec @@ -100,6 +102,9 @@ account: officielles seront utilisées à la place. pokemon-news-custom-opt-in-off: Actualités Pokémon officielles uniquement pokemon-news-custom-opt-in-on: Actualités Pokémon personnalisées lorsqu’elles sont disponibles + time-zone: Préférence de fuseau horaire + time-zone-blurb: Le fuseau horaire à utiliser pour les horodatages dans les jeux. + Notez bien que quelques horodatages peut s'afficher en UTC+9, en fonction du jeu et du contexte. index: what-is-reon: Qu'est-ce c'est REON? reon-description: REON est quelque chose. diff --git a/web/templates/user/summary.twig b/web/templates/user/summary.twig index 9b2dd27..b7b45c6 100644 --- a/web/templates/user/summary.twig +++ b/web/templates/user/summary.twig @@ -120,6 +120,20 @@

{{ "invalid-pokemon-news-setting"|trans }}

{% endif %} + +
+ +

{{ "account.time-zone-blurb"|trans }}

+ + {% if ("timeZoneValue" in errors) %} +

{{ "invalid-time-zone-setting"|trans }}

+ {% endif %} +