diff --git a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/ElunaSettings.php b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/ElunaSettings.php index 6ba0396cd..1cfaa810f 100644 --- a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/ElunaSettings.php +++ b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/ElunaSettings.php @@ -6,6 +6,7 @@

page_alias) ?>

Configure database connection for Eluna script that need use of the CMS.

+
diff --git a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/PVPRewards.php b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/PVPRewards.php index 101ce4a6b..ef1beea80 100644 --- a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/PVPRewards.php +++ b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/PVPRewards.php @@ -14,6 +14,7 @@
Give rewards

+ diff --git a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/RealmSettings.php b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/RealmSettings.php index d99ff9f7e..5ac88f32a 100644 --- a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/RealmSettings.php +++ b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/RealmSettings.php @@ -1,254 +1,392 @@

page_alias) ?>

Configure realm name and database connection.

- -
-
-
- General Settings -
-
-
- - - - - - - - -

- First time using SOAP? Click me! + +

+ + +
+ + +
+
+
General Settings
+
+ + + + + + + + +

First time using SOAP? Click me!

+
+
+ SOAP Settings + acore_soap_host && Opts::I()->acore_soap_user): ?> + Checking… + +
+ + + + + + + + + + + + + + + + + + + + +
+
Database: Auth
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
Database: Characters
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
Database: World
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +

+ +

-
- -
- SOAP Settings -
- - - - - - - - - - - - - - - - - - - - - -
- - -
- Database: Auth -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
- Database: Characters -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
- Database: World -
- - - - - - - - - - - - - - - - - - - - - - - - -
+
You will need to "Save Changes" above before checking your SOAP Configuration!
+
-
+ + - -
+ + + +
+
diff --git a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php index 86058a205..251f61627 100644 --- a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php +++ b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php @@ -1,16 +1,83 @@ acore_resurrection_scroll == '1'; ?> @@ -21,20 +88,19 @@

Tools


+
-
+ + +
-
- Worldserver integration -
+
World Server Integration

- + - acore_resurrection_scroll != '1') echo 'style="display:none;"'?>> - + + @@ -67,91 +157,452 @@ -
+ +
-
- Name Unlock Settings -
+
Web Integration
+
+
+ + + + + + + + + + + + + + + + +
+ + +

Remove 2FA

+

+ Remove Website or In-game 2FA for any account. A warning is shown to the user until they re-enable it. +

+ + +

Website

+
+ + + +
+

+ + +
+

Backup Codes

+ Check a Website account above to view backup codes. + +
+ + +

In-Game

+
+ + + +
+

+ +
+
+
+ + +
+
+
+
Name Unlock Settings

Allowed banned names table (characters database): -
-
+ value="acore_name_unlock_allowed_banned_names_table) ?>"> +

Inactivity Thresholds per Level: + - + -
Add
+
+
+ Add +
+
+ Reset +
+
+
+ +
+ + +
+
+
User Login History
+
+

+ Look up the recorded login IP history for any account (the same list the user sees on their Security page). +

+
+ + +
+

+ + + + + + +

+ +

-
- -
+

+ +

+ + + diff --git a/src/acore-wp-plugin/src/Components/AdminPanel/SettingsController.php b/src/acore-wp-plugin/src/Components/AdminPanel/SettingsController.php index b14b73694..dde69a8b6 100644 --- a/src/acore-wp-plugin/src/Components/AdminPanel/SettingsController.php +++ b/src/acore-wp-plugin/src/Components/AdminPanel/SettingsController.php @@ -44,6 +44,8 @@ public function loadSettings() { // If they did, this hidden field will be set to 'Y' if ($_SERVER['REQUEST_METHOD'] == 'POST') { + check_admin_referer('acore_realm_settings_save', 'acore_realm_settings_nonce'); + foreach (Opts::I()->getConfs() as $key => $value) { if (isset($_POST[$key])) { $this->storeConf($key, $_POST[$key]); @@ -84,6 +86,8 @@ public function loadElunaSettings() { // If they did, this hidden field will be set to 'Y' if ($_SERVER['REQUEST_METHOD'] == 'POST') { + check_admin_referer('acore_eluna_settings_save', 'acore_eluna_settings_nonce'); + foreach (Opts::I()->getConfs() as $key => $value) { if (isset($_POST[$key])) { $this->storeConf($key, $_POST[$key]); @@ -131,6 +135,7 @@ public function loadPvpRewards() { //! DEV NOTE: Put the rest of stuff within try { ... } check to handle every exception properly try { if ($_SERVER['REQUEST_METHOD'] == 'POST') { + check_admin_referer('acore_pvp_rewards_save', 'acore_pvp_rewards_nonce'); global $wpdb; $tableResult = $wpdb->query("CREATE TEMPORARY TABLE temp_pvp_rewards ( `account` VARCHAR(255) COLLATE utf8_unicode_ci, @@ -385,6 +390,13 @@ public function loadTools() { //! DEV NOTE: Put the rest of stuff within try { ... } check to handle every exception properly try { if ($_SERVER['REQUEST_METHOD'] == 'POST') { + check_admin_referer('acore_tools_save', 'acore_tools_nonce'); + + // If the thresholds sentinel is present but no rows were submitted, clear them + if (isset($_POST['acore_name_unlock_thresholds_present']) && !isset($_POST['acore_name_unlock_thresholds'])) { + $this->storeConf('acore_name_unlock_thresholds', []); + } + foreach (Opts::I()->getConfs() as $key => $value) { if (isset($_POST[$key])) { if ($key == 'acore_name_unlock_allowed_banned_names_table') { diff --git a/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersController.php b/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersController.php index 98dee522d..3619c3a76 100644 --- a/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersController.php +++ b/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersController.php @@ -14,30 +14,81 @@ public function __construct() { public function loadHome() { if ($_SERVER["REQUEST_METHOD"] == "POST") { - $this->saveCharacterOrder(); - ?> -

Character settings succesfully saved.

- resetCharacterOrder(); + ?> +

Character order reset successfully.

+ saveCharacterOrder(); + ?> +

Character settings successfully saved.

+ getAcoreAccountId(); + if (!$accId) { + echo $this->getView()->getHomeRender([], 0, null); + return; + } + + $conn = ACoreServices::I()->getCharacterEm()->getConnection(); + $query = "SELECT - `guid`, `name`, `order`, `race`, `class`, `level`, `gender` - FROM `characters` - WHERE `characters`.`deleteDate` IS NULL AND `account` = $accId - ORDER BY COALESCE(`order`, `guid`) + c.`guid`, c.`name`, c.`order`, c.`race`, c.`class`, c.`level`, c.`gender`, + cb.`bandate` AS `ban_bandate`, + cb.`unbandate` AS `ban_unbandate` + FROM `characters` c + LEFT JOIN `character_banned` cb + ON cb.`guid` = c.`guid` + AND cb.`active` = 1 + AND (cb.`unbandate` = 0 OR cb.`unbandate` = cb.`bandate` OR cb.`unbandate` > UNIX_TIMESTAMP()) + WHERE c.`deleteDate` IS NULL AND c.`account` = ? + ORDER BY (c.`order` IS NULL), c.`order`, c.`guid` "; - $conn = ACoreServices::I()->getCharacterEm()->getConnection(); - $queryResult = $conn->executeQuery($query); - $chars = $queryResult->fetchAllAssociative(); + $chars = $conn->executeQuery($query, [$accId])->fetchAllAssociative(); + + $authConn = ACoreServices::I()->getAccountEm()->getConnection(); + + $muteRow = $authConn + ->executeQuery("SELECT `mutetime` FROM `account` WHERE `id` = ?", [$accId]) + ->fetchAssociative(); + $mutetime = $muteRow ? intval($muteRow['mutetime']) : 0; + // Negative = pending mute (seconds magnitude, applied on next login); positive = Unix timestamp expiry - echo $this->getView()->getHomeRender($chars); + $accBanRow = $authConn + ->executeQuery( + "SELECT `bandate`, `unbandate` FROM `account_banned` + WHERE `id` = ? AND `active` = 1 + AND (`unbandate` = 0 OR `unbandate` = `bandate` OR `unbandate` > UNIX_TIMESTAMP()) + ORDER BY `bandate` DESC LIMIT 1", + [$accId] + ) + ->fetchAssociative(); + + echo $this->getView()->getHomeRender($chars, $mutetime, $accBanRow); } public function getView() { return $this->view; } + private function resetCharacterOrder() { + $accId = ACoreServices::I()->getAcoreAccountId(); + $accId = is_numeric($accId) ? (int) $accId : 0; + if ($accId <= 0) { + return; + } + $conn = ACoreServices::I()->getCharacterEm()->getConnection(); + $stmt = $conn->prepare( + "UPDATE `characters` SET `order` = NULL WHERE `account` = ? AND `deleteDate` IS NULL" + ); + $stmt->bindValue(1, $accId); + $stmt->executeQuery(); + } + private function saveCharacterOrder() { if (!isset($_POST) || !isset($_POST["characterorder"])) { return; diff --git a/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersMenu.php b/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersMenu.php index c3a6da3af..3cd9c6f5d 100644 --- a/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersMenu.php +++ b/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersMenu.php @@ -3,6 +3,7 @@ namespace ACore\Components\CharactersMenu; use ACore\Components\CharactersMenu\CharactersController; +use ACore\Manager\ACoreServices; add_action('init', __NAMESPACE__ . '\\characters_menu_init'); @@ -25,7 +26,35 @@ public static function I() function acore_characters_menu() { - add_submenu_page('profile.php', 'Characters', 'Characters', 'read', ACORE_SLUG . '-characters-menu', array($this, 'acore_characters_menu_page')); + $menuTitle = 'Characters'; + try { + $accId = ACoreServices::I()->getAcoreAccountId(); + if (!$accId) throw new \Exception('no account'); + $authConn = ACoreServices::I()->getAccountEm()->getConnection(); + $now = time(); + + $isBanned = (bool) $authConn->executeQuery( + "SELECT 1 FROM `account_banned` + WHERE `id` = ? AND `active` = 1 + AND (`unbandate` = 0 OR `unbandate` = `bandate` OR `unbandate` > UNIX_TIMESTAMP()) + LIMIT 1", [$accId] + )->fetchOne(); + + if ($isBanned) { + $menuTitle .= ' Banned'; + } else { + $muteRow = $authConn->executeQuery("SELECT `mutetime` FROM `account` WHERE `id` = ?", [$accId])->fetchAssociative(); + $mutetime = $muteRow ? intval($muteRow['mutetime']) : 0; + $isMuted = $mutetime < 0 || $mutetime > $now; + if ($isMuted) { + $menuTitle .= ' Muted'; + } + } + } catch (\Throwable $e) { + // silently skip badge on DB error + } + + add_submenu_page('profile.php', 'Characters', $menuTitle, 'read', ACORE_SLUG . '-characters-menu', array($this, 'acore_characters_menu_page')); } function acore_characters_menu_page() @@ -40,4 +69,7 @@ function characters_menu_init() $charactersMenu = CharactersMenu::I(); add_action('admin_menu', array($charactersMenu, 'acore_characters_menu')); + add_action('admin_head', function () { + echo ''; + }); } diff --git a/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php b/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php index 12d98ae34..f32c40cb2 100644 --- a/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php +++ b/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php @@ -2,6 +2,8 @@ namespace ACore\Components\CharactersMenu; +use ACore\Utils\AcoreCharColors; + class CharactersView { private $controller; @@ -13,47 +15,119 @@ public function __construct($controller) { $this->controller = $controller; } - public function getHomeRender($characters) { + private function formatDuration($seconds) { + $seconds = abs(intval($seconds)); + if ($seconds >= 31536000) { $n = intdiv($seconds, 31536000); return $n . ' ' . ($n === 1 ? 'year' : 'years'); } + if ($seconds >= 2592000) { $n = intdiv($seconds, 2592000); return $n . ' ' . ($n === 1 ? 'month' : 'months'); } + if ($seconds >= 86400) { $n = intdiv($seconds, 86400); return $n . ' ' . ($n === 1 ? 'day' : 'days'); } + if ($seconds >= 3600) { $n = intdiv($seconds, 3600); return $n . ' ' . ($n === 1 ? 'hour' : 'hours'); } + if ($seconds >= 60) { $n = intdiv($seconds, 60); return $n . ' ' . ($n === 1 ? 'minute' : 'minutes'); } + return $seconds . ' ' . ($seconds === 1 ? 'second' : 'seconds'); + } + + private function formatDate($ts) { return date('d-m-Y', intval($ts)); } + private function formatTime($ts) { return date('H:i', intval($ts)); } + + public function getHomeRender($characters, $mutetime = 0, $accBanRow = null) { + $now = time(); + + // Account mute + $isMuted = $mutetime < 0 || $mutetime > $now; + $mutePending = $mutetime < 0; + $muteRemaining = $mutePending ? abs($mutetime) : max(0, $mutetime - $now); + + // Account ban + $isAccountBanned = !empty($accBanRow); + $accBanPerma = $isAccountBanned && (intval($accBanRow['unbandate']) === 0 || $accBanRow['unbandate'] === $accBanRow['bandate']); + $accBanRemaining = ($isAccountBanned && !$accBanPerma) ? max(0, intval($accBanRow['unbandate']) - $now) : 0; + ob_start(); wp_enqueue_style('bootstrap-css', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', array(), '5.1.3'); - wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.1'); + wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.5'); wp_enqueue_script('bootstrap-js', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js', array(), '5.1.3'); wp_enqueue_script('jquery-ui-sortable'); ?>
-

Characters Settings

-

Check some details and configure of your characters.

-
-
+
-

Order

-

Change the order in which the characters appear in your in-game character selection screen.

+

Order Character Screen

+

Change the order in which your characters appear in the in-game selection screen by dragging them into your preferred position.

+ + +
+ -
    - -
  • - @@ -62,12 +136,136 @@ public function getHomeRender($characters) {
- + $("#acore-characters-order").sortable({ + update: function(event, ui) { + var order = []; + $("#acore-characters-order li").each(function(index) { + var input = $(this).find("input[name='characterorder[]']"); + order.push(input.val()); + $(this).find(".acore-char-pos").text(index + 1); + }); + } + }); + $("#acore-characters-order").disableSelection(); + }); + acore_db_world_name; $itemQuery = "SELECT mi.`mail_id`, ii.`itemEntry`, ii.`count`, - it.`name` AS item_name + it.`name` AS item_name, it.`Quality` AS item_quality FROM `mail_items` mi JOIN `item_instance` ii ON mi.`item_guid` = ii.`guid` JOIN `$worldDb`.`item_template` it ON ii.`itemEntry` = it.`entry` diff --git a/src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnView.php b/src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnView.php index 5a882e5c2..c75b11169 100644 --- a/src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnView.php +++ b/src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnView.php @@ -2,6 +2,8 @@ namespace ACore\Components\MailReturnMenu; +use ACore\Utils\AcoreCharColors; + class MailReturnView { @@ -17,58 +19,80 @@ public function getMailReturnRender($chars) ob_start(); wp_enqueue_style('bootstrap-css', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', array(), '5.1.3'); - wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.1'); + wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.5'); wp_enqueue_script('bootstrap-js', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js', array(), '5.1.3'); wp_enqueue_script('jquery'); - wp_enqueue_script('acore-mail-return-js', ACORE_URL_PLG . 'web/assets/mail-return/mail-return.js', array('jquery'), null, true); + wp_enqueue_script('power-js', 'https://wow.zamimg.com/widgets/power.js', array(), null, false); + wp_enqueue_script('acore-mail-return-js', ACORE_URL_PLG . 'web/assets/mail-return/mail-return.js', array('jquery'), '2.3', true); + wp_localize_script('acore-mail-return-js', 'mailReturnData', [ + 'mailsUrl' => rest_url(ACORE_SLUG . '/v1/mail-return/list'), + 'returnUrl' => rest_url(ACORE_SLUG . '/v1/mail-return'), + 'assetsUrl' => ACORE_URL_PLG . 'web/assets/', + 'nonce' => wp_create_nonce('wp_rest'), + ]); ?> -
-
-
-
-

Mail Return

-

You can return sent mails that have not yet been read by the recipient in this page. Select the character, the sent mail and hit return.

-
- - - - -