Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,18 @@ public function __construct() {

public function loadHome() {
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$this->saveCharacterOrder();
?>
<div class="updated"><p><strong>Character settings succesfully saved.</strong></p></div>
<?php
check_admin_referer('acore_character_order', 'acore_character_order_nonce');
if (isset($_POST["acore_reset_order"])) {
$this->resetCharacterOrder();
?>
<div class="updated"><p><strong>Character order reset successfully.</strong></p></div>
<?php
} else {
$this->saveCharacterOrder();
?>
<div class="updated"><p><strong>Character settings succesfully saved.</strong></p></div>
<?php
}
}

$accId = ACoreServices::I()->getAcoreAccountId();
Expand All @@ -38,6 +46,16 @@ public function getView() {
return $this->view;
}

private function resetCharacterOrder() {
$accId = ACoreServices::I()->getAcoreAccountId();
$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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace ACore\Components\CharactersMenu;

use ACore\Utils\AcoreCharColors;

class CharactersView {
private $controller;

Expand All @@ -16,44 +18,47 @@ public function __construct($controller) {
public function getHomeRender($characters) {
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');
?>
<div class="wrap">
<h2>Characters Settings</h2>
<p>Check some details and configure of your characters.</p>

<div class="row">
<div class="col-sm-4">
<div class="card">
<div class="card-body">
<h3>Order</h3>
<p>Change the order in which the characters appear in your in-game character selection screen.</p>
<h3>Order Character Screen</h3>
<p>Change the order in which the characters appear in your in-game character selection screen, by dragging them, matches in-game position.</p>
<hr>
<form action="" method="POST" novalidate="novalidate">
<?php wp_nonce_field('acore_character_order', 'acore_character_order_nonce'); ?>

<ul id="acore-characters-order" class="list-unstyled">
<?php foreach ($characters as $char) { ?>
<ul id="acore-characters-order" class="acore-char-list list-unstyled">
<?php $charPos = 0; foreach ($characters as $char) {
$clsStyle = AcoreCharColors::rowStyle(intval($char["class"]), intval($char["race"]));
$charPos++;
$displayPos = $char['order'] !== null ? intval($char['order']) + 1 : $charPos;
?>
<li>
<div class="menu-item-bar">
<div class="menu-item-handle ui-sortable-handle">
<span class="item-title menu-item-title"><?= $char["name"] ?></span>
<span class="item-controls">
<span class="item-type">
level <?= $char["level"] ?>&ensp;
<img src="<?= ACORE_URL_PLG . "web/assets/race/" . $char["race"] . ($char["gender"] == 0 ? "m" : "f") . ".webp"; ?>">
<img src="<?= ACORE_URL_PLG . "web/assets/class/" . $char["class"] . ".webp"; ?>">
</span>
</div>
<div class="acore-char-row" style="<?= esc_attr($clsStyle) ?>">
<span class="acore-char-pos"><?= $displayPos ?></span>
<span class="acore-char-name"><?= esc_html($char["name"]) ?></span>
<span class="acore-char-meta">
<span class="acore-level" data-exp="<?= AcoreCharColors::expansionSlug(intval($char["level"])) ?>" title="<?= esc_attr(AcoreCharColors::expansionLabel(intval($char["level"]))) ?>">Level <?= intval($char["level"]) ?></span>
<img class="race-icon" height="32" width="32" title="<?= esc_attr(AcoreCharColors::getRaceName(intval($char["race"]))) ?>" src="<?= esc_url(ACORE_URL_PLG . "web/assets/race/" . intval($char["race"]) . (intval($char["gender"]) == 0 ? "m" : "f") . ".webp") ?>">
<img class="class-icon" height="32" width="32" title="<?= esc_attr(AcoreCharColors::getClassName(intval($char["class"]))) ?>" src="<?= esc_url(ACORE_URL_PLG . "web/assets/class/" . intval($char["class"]) . ".webp") ?>">
Comment thread
TheSCREWEDSoftware marked this conversation as resolved.
</span>
</div>
<input name="characterorder[]" value="<?= $char["guid"] ?>">
<input type="hidden" name="characterorder[]" value="<?= esc_attr($char["guid"]) ?>">
</li>
<?php } ?>
</ul>

<?php if (!empty($characters)) { ?>
<input type="submit" name="submit" class="button button-primary" value="Save Order">
<div style="display:flex; justify-content:space-between; align-items:center; margin-top:8px;">
<input type="submit" name="submit" class="button button-primary" value="Save Order">
<input type="submit" name="acore_reset_order" class="button acore-btn-danger" value="Reset Order">
</div>
<?php } ?>
</form>
</div>
Expand All @@ -62,12 +67,23 @@ public function getHomeRender($characters) {
</div>
</div>

<script type="text/javascript">
jQuery(document).ready(function($) {
$("#acore-characters-order").sortable();
<!-- .acore-btn-danger styling lives in theme.css (shared light/dark) -->

<script>
jQuery(document).ready(function($) {
$("#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();
});
</script>

<?php
return ob_get_clean();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static function getSentUnreadMails($charGuid)

// Get mails sent by this character that are unread by the receiver
// messageType = 0 means player mail
$query = "SELECT m.`id`, m.`subject`, m.`has_items`, m.`money`,
$query = "SELECT m.`id`, m.`subject`, m.`has_items`, m.`money`, m.`cod`,
m.`expire_time`, m.`deliver_time`,
rc.`name` AS receiver_name, rc.`race` AS receiver_race,
rc.`class` AS receiver_class, rc.`gender` AS receiver_gender,
Expand Down Expand Up @@ -94,7 +94,7 @@ public static function getSentUnreadMails($charGuid)
$placeholders = implode(',', array_fill(0, count($mailIds), '?'));
$worldDb = Opts::I()->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`
Expand Down
108 changes: 66 additions & 42 deletions src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnView.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace ACore\Components\MailReturnMenu;

use ACore\Utils\AcoreCharColors;


class MailReturnView
{
Expand All @@ -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'),
]);

?>

<div class="wrap">
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<h3>Mail Return</h3>
<p>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.</p>
<hr>

<label for="mail-return-char-select"><strong>Select Character:</strong></label>
<select id="mail-return-char-select" class="form-select mb-3">
<option value="">-- Select a character --</option>
<?php foreach ($chars as $char) { ?>
<option value="<?= intval($char["guid"]) ?>" data-name="<?= esc_attr($char["name"]) ?>">
<?= esc_html($char["name"]) ?> (Level <?= intval($char["level"]) ?>)
</option>
<?php } ?>
</select>

<div id="mail-return-loading" style="display:none;" class="text-center my-3">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
<script>const whTooltips = {colorLinks: true, iconizeLinks: true, renameLinks: true};</script>
<div class="wrap" id="acore-mail-return-page">
<div id="mail-return-layout">

<!-- Col 1: character selector - always visible, same width -->
<div id="mail-return-sidebar">
<div class="card">
<div class="card-body">
<h3>Mail Return</h3>
<p>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.</p>
<hr>

<strong>Select Character:</strong>
<ul id="acore-characters-mail" class="acore-char-list list-unstyled mt-2">
Comment thread
TheSCREWEDSoftware marked this conversation as resolved.
<?php foreach ($chars as $char) {
$clsStyle = AcoreCharColors::rowStyle(intval($char['class']), intval($char['race']));
?>
<li>
<button type="button" class="acore-char-row acore-char-card" data-char-guid="<?= intval($char['guid']) ?>" style="<?= esc_attr($clsStyle) ?>">
<span class="acore-char-name"><?= esc_html($char['name']) ?></span>
<span class="acore-char-meta">
<span class="acore-level" data-exp="<?= AcoreCharColors::expansionSlug(intval($char['level'])) ?>" title="<?= esc_attr(AcoreCharColors::expansionLabel(intval($char['level']))) ?>">Level <?= intval($char['level']) ?></span>
<img class="race-icon" height="32" width="32" alt="<?= esc_attr(AcoreCharColors::getRaceName(intval($char['race']))) ?>" title="<?= esc_attr(AcoreCharColors::getRaceName(intval($char['race']))) ?>" src="<?= ACORE_URL_PLG . 'web/assets/race/' . intval($char['race']) . (intval($char['gender']) == 0 ? 'm' : 'f') . '.webp' ?>">
<img class="class-icon" height="32" width="32" alt="<?= esc_attr(AcoreCharColors::getClassName(intval($char['class']))) ?>" title="<?= esc_attr(AcoreCharColors::getClassName(intval($char['class']))) ?>" src="<?= ACORE_URL_PLG . 'web/assets/class/' . intval($char['class']) . '.webp' ?>">
</span>
</button>
</li>
<?php } ?>
</ul>

<div id="mail-return-loading" style="display:none;" class="text-center my-3">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>

<!-- Empty state lives inside the card -->
<div id="mail-return-empty-wrap" style="display:none;">
<hr>
<h5>Unread Sent Mails</h5>
<p id="mail-return-empty" class="text-muted">No unread sent mails found for this character.</p>
</div>
</div>
</div>
</div><!-- /mail-return-sidebar -->

<div id="mail-return-list" style="display:none;">
<h5>Unread Sent Mails</h5>
<ul id="mail-return-items" class="list-unstyled"></ul>
<p id="mail-return-empty" style="display:none;" class="text-muted">No unread sent mails found for this character.</p>
<!-- Cols 2-3: mail list - hidden until mails are loaded by JS -->
<div id="mail-return-content">
<div class="card">
<div class="card-body">
<h5 id="mail-return-heading">Unread Sent Mails</h5>
<ul id="mail-return-items" class="list-unstyled mb-0"></ul>
</div>
</div>
</div>
</div>
</div>

<script>
var mailReturnData = {
nonce: "<?php echo esc_js(wp_create_nonce('wp_rest')); ?>",
mailsUrl: "<?php echo esc_url(get_rest_url(null, ACORE_SLUG . '/v1/mail-return/list')); ?>",
returnUrl: "<?php echo esc_url(get_rest_url(null, ACORE_SLUG . '/v1/mail-return')); ?>",
assetsUrl: "<?php echo esc_url(ACORE_URL_PLG . 'web/assets/'); ?>"
};
</script>
<script>var whTooltips = {colorLinks: true, iconizeLinks: true, renameLinks: true};</script>
<script src="https://wow.zamimg.com/js/tooltips.js"></script>
<?php
</div><!-- /mail-return-content -->

</div><!-- /mail-return-layout -->
</div><!-- /wrap -->

<?php
return ob_get_clean();
}
}
18 changes: 13 additions & 5 deletions src/acore-wp-plugin/src/Components/Tools/ToolsApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,31 @@ public static function ItemRestoreList($request) {
}

public static function ItemRestore($data) {
$item = $data['item'];
$cname = $data['cname'];
$item = filter_var($data['item'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
if ($item === false) {
return new \WP_Error('invalid_item', 'Invalid item id.', ['status' => 400]);
}
if (!ACoreServices::I()->currentAccountOwnsCharacterName($cname)) {
return new \WP_Error('forbidden', 'You do not own that character.', ['status' => 403]);
}
return ACoreServices::I()->getServerSoap()->executeCommand("item restore $item $cname");
Comment thread
TheSCREWEDSoftware marked this conversation as resolved.
}
}

add_action( 'rest_api_init', function () {
register_rest_route( ACORE_SLUG . '/v1', 'item-restore/list/(?P<cguid>\d+)', array(
'methods' => 'GET',
'callback' => function( $request ) {
'methods' => 'GET',
'permission_callback' => function() { return is_user_logged_in(); },
'callback' => function( $request ) {
return ToolsApi::ItemRestoreList($request);
Comment on lines +30 to 31

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Convert denied list requests into REST errors.

getRestorableItemsByCharacter() now throws for invalid or unowned GUIDs; this callback returns it directly, so probing another character can surface as an uncaught exception/500 instead of a controlled REST response.

Proposed REST error handling
        'permission_callback' => function() { return is_user_logged_in(); },
        'callback'            => function( $request ) {
-            return ToolsApi::ItemRestoreList($request);
+            try {
+                return ToolsApi::ItemRestoreList($request);
+            } catch (\InvalidArgumentException $e) {
+                return new \WP_Error('invalid_character', 'Character not found.', ['status' => 404]);
+            }
        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/acore-wp-plugin/src/Components/Tools/ToolsApi.php` around lines 30 - 31,
The callback function for the REST endpoint does not handle exceptions that may
be thrown by ToolsApi::ItemRestoreList() when getRestorableItemsByCharacter()
encounters invalid or unowned GUIDs. Wrap the call to
ToolsApi::ItemRestoreList($request) in a try-catch block to catch any
exceptions, and in the catch block, convert them into proper REST error
responses using appropriate WordPress REST API error handling functions so that
exceptions result in controlled error responses rather than uncaught 500 errors.

}
));

register_rest_route( ACORE_SLUG . '/v1', 'item-restore', array(
'methods' => 'POST',
'callback' => function( $request ) {
'methods' => 'POST',
'permission_callback' => function() { return is_user_logged_in(); },
'callback' => function( $request ) {

// if free item-restoration is disabled, return
if (Opts::I()->acore_item_restoration != '1') {
Expand Down
Loading
Loading