Skip to content

Commit 45c4ee6

Browse files
[5 / 7] - Unstuck / Scroll of Resurrection / RAF (#202)
Split from the main PR: #197 Split by Claude Unstuck restyled to the new rows; Scroll of Resurrection gets green/red/yellow status colours; RAF (recruit-a-friend) gets a CSRF nonce + validation. Minor shared-service tidy. <img width="1920" height="1030" alt="image" src="https://github.com/user-attachments/assets/97b69047-870e-41bd-878d-5c52a665384a" /> <img width="1920" height="1030" alt="image" src="https://github.com/user-attachments/assets/dca4c6e3-b29d-4175-a227-84e7d52a730a" /> Not testde the RAF page, as the changes were made by Rabbit's review. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Dark mode toggle in WordPress admin bar * Character order reset functionality in character management * Item quality indicators in mail and restoration displays * **Improvements** * Redesigned character management UI with card-based layout, icons, and metadata * Revamped mail return interface with character sidebar navigation * Reimagined item restoration page with card grid and improved organization * Enhanced security with nonce validation and permission checks across endpoints * Improved visual styling with expanded theme and dark mode CSS * **Bug Fixes** * Module availability validation for Resurrection Scroll feature <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 94af292 commit 45c4ee6

19 files changed

Lines changed: 2765 additions & 409 deletions

File tree

src/acore-wp-plugin/src/Components/CharactersMenu/CharactersController.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,18 @@ public function __construct() {
1414

1515
public function loadHome() {
1616
if ($_SERVER["REQUEST_METHOD"] == "POST") {
17-
$this->saveCharacterOrder();
18-
?>
19-
<div class="updated"><p><strong>Character settings succesfully saved.</strong></p></div>
20-
<?php
17+
check_admin_referer('acore_character_order', 'acore_character_order_nonce');
18+
if (isset($_POST["acore_reset_order"])) {
19+
$this->resetCharacterOrder();
20+
?>
21+
<div class="updated"><p><strong>Character order reset successfully.</strong></p></div>
22+
<?php
23+
} else {
24+
$this->saveCharacterOrder();
25+
?>
26+
<div class="updated"><p><strong>Character settings succesfully saved.</strong></p></div>
27+
<?php
28+
}
2129
}
2230

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

49+
private function resetCharacterOrder() {
50+
$accId = ACoreServices::I()->getAcoreAccountId();
51+
$conn = ACoreServices::I()->getCharacterEm()->getConnection();
52+
$stmt = $conn->prepare(
53+
"UPDATE `characters` SET `order` = NULL WHERE `account` = ? AND `deleteDate` IS NULL"
54+
);
55+
$stmt->bindValue(1, $accId);
56+
$stmt->executeQuery();
57+
}
58+
4159
private function saveCharacterOrder() {
4260
if (!isset($_POST) || !isset($_POST["characterorder"])) {
4361
return;

src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace ACore\Components\CharactersMenu;
44

5+
use ACore\Utils\AcoreCharColors;
6+
57
class CharactersView {
68
private $controller;
79

@@ -16,44 +18,47 @@ public function __construct($controller) {
1618
public function getHomeRender($characters) {
1719
ob_start();
1820
wp_enqueue_style('bootstrap-css', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', array(), '5.1.3');
19-
wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.1');
21+
wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.5');
2022
wp_enqueue_script('bootstrap-js', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js', array(), '5.1.3');
2123
wp_enqueue_script('jquery-ui-sortable');
2224
?>
2325
<div class="wrap">
24-
<h2>Characters Settings</h2>
25-
<p>Check some details and configure of your characters.</p>
26-
2726
<div class="row">
2827
<div class="col-sm-4">
2928
<div class="card">
3029
<div class="card-body">
31-
<h3>Order</h3>
32-
<p>Change the order in which the characters appear in your in-game character selection screen.</p>
30+
<h3>Order Character Screen</h3>
31+
<p>Change the order in which the characters appear in your in-game character selection screen, by dragging them, matches in-game position.</p>
3332
<hr>
3433
<form action="" method="POST" novalidate="novalidate">
34+
<?php wp_nonce_field('acore_character_order', 'acore_character_order_nonce'); ?>
3535

36-
<ul id="acore-characters-order" class="list-unstyled">
37-
<?php foreach ($characters as $char) { ?>
36+
<ul id="acore-characters-order" class="acore-char-list list-unstyled">
37+
<?php $charPos = 0; foreach ($characters as $char) {
38+
$clsStyle = AcoreCharColors::rowStyle(intval($char["class"]), intval($char["race"]));
39+
$charPos++;
40+
$displayPos = $char['order'] !== null ? intval($char['order']) + 1 : $charPos;
41+
?>
3842
<li>
39-
<div class="menu-item-bar">
40-
<div class="menu-item-handle ui-sortable-handle">
41-
<span class="item-title menu-item-title"><?= $char["name"] ?></span>
42-
<span class="item-controls">
43-
<span class="item-type">
44-
level <?= $char["level"] ?>&ensp;
45-
<img src="<?= ACORE_URL_PLG . "web/assets/race/" . $char["race"] . ($char["gender"] == 0 ? "m" : "f") . ".webp"; ?>">
46-
<img src="<?= ACORE_URL_PLG . "web/assets/class/" . $char["class"] . ".webp"; ?>">
47-
</span>
48-
</div>
43+
<div class="acore-char-row" style="<?= esc_attr($clsStyle) ?>">
44+
<span class="acore-char-pos"><?= $displayPos ?></span>
45+
<span class="acore-char-name"><?= esc_html($char["name"]) ?></span>
46+
<span class="acore-char-meta">
47+
<span class="acore-level" data-exp="<?= AcoreCharColors::expansionSlug(intval($char["level"])) ?>" title="<?= esc_attr(AcoreCharColors::expansionLabel(intval($char["level"]))) ?>">Level <?= intval($char["level"]) ?></span>
48+
<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") ?>">
49+
<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") ?>">
50+
</span>
4951
</div>
50-
<input name="characterorder[]" value="<?= $char["guid"] ?>">
52+
<input type="hidden" name="characterorder[]" value="<?= esc_attr($char["guid"]) ?>">
5153
</li>
5254
<?php } ?>
5355
</ul>
5456

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

65-
<script type="text/javascript">
66-
jQuery(document).ready(function($) {
67-
$("#acore-characters-order").sortable();
70+
<!-- .acore-btn-danger styling lives in theme.css (shared light/dark) -->
71+
72+
<script>
73+
jQuery(document).ready(function($) {
74+
$("#acore-characters-order").sortable({
75+
update: function(event, ui) {
76+
var order = [];
77+
$("#acore-characters-order li").each(function(index) {
78+
var input = $(this).find("input[name='characterorder[]']");
79+
order.push(input.val());
80+
$(this).find(".acore-char-pos").text(index + 1);
81+
});
82+
}
6883
});
84+
$("#acore-characters-order").disableSelection();
85+
});
6986
</script>
70-
7187
<?php
7288
return ob_get_clean();
7389
}

src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public static function getSentUnreadMails($charGuid)
6464

6565
// Get mails sent by this character that are unread by the receiver
6666
// messageType = 0 means player mail
67-
$query = "SELECT m.`id`, m.`subject`, m.`has_items`, m.`money`,
67+
$query = "SELECT m.`id`, m.`subject`, m.`has_items`, m.`money`, m.`cod`,
6868
m.`expire_time`, m.`deliver_time`,
6969
rc.`name` AS receiver_name, rc.`race` AS receiver_race,
7070
rc.`class` AS receiver_class, rc.`gender` AS receiver_gender,
@@ -94,7 +94,7 @@ public static function getSentUnreadMails($charGuid)
9494
$placeholders = implode(',', array_fill(0, count($mailIds), '?'));
9595
$worldDb = Opts::I()->acore_db_world_name;
9696
$itemQuery = "SELECT mi.`mail_id`, ii.`itemEntry`, ii.`count`,
97-
it.`name` AS item_name
97+
it.`name` AS item_name, it.`Quality` AS item_quality
9898
FROM `mail_items` mi
9999
JOIN `item_instance` ii ON mi.`item_guid` = ii.`guid`
100100
JOIN `$worldDb`.`item_template` it ON ii.`itemEntry` = it.`entry`

src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnView.php

Lines changed: 66 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace ACore\Components\MailReturnMenu;
44

5+
use ACore\Utils\AcoreCharColors;
6+
57

68
class MailReturnView
79
{
@@ -17,58 +19,80 @@ public function getMailReturnRender($chars)
1719
ob_start();
1820

1921
wp_enqueue_style('bootstrap-css', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', array(), '5.1.3');
20-
wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.1');
22+
wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.5');
2123
wp_enqueue_script('bootstrap-js', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js', array(), '5.1.3');
2224
wp_enqueue_script('jquery');
23-
wp_enqueue_script('acore-mail-return-js', ACORE_URL_PLG . 'web/assets/mail-return/mail-return.js', array('jquery'), null, true);
25+
wp_enqueue_script('power-js', 'https://wow.zamimg.com/widgets/power.js', array(), null, false);
26+
wp_enqueue_script('acore-mail-return-js', ACORE_URL_PLG . 'web/assets/mail-return/mail-return.js', array('jquery'), '2.3', true);
27+
wp_localize_script('acore-mail-return-js', 'mailReturnData', [
28+
'mailsUrl' => rest_url(ACORE_SLUG . '/v1/mail-return/list'),
29+
'returnUrl' => rest_url(ACORE_SLUG . '/v1/mail-return'),
30+
'assetsUrl' => ACORE_URL_PLG . 'web/assets/',
31+
'nonce' => wp_create_nonce('wp_rest'),
32+
]);
2433

2534
?>
2635

27-
<div class="wrap">
28-
<div class="col-sm-6">
29-
<div class="card">
30-
<div class="card-body">
31-
<h3>Mail Return</h3>
32-
<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>
33-
<hr>
34-
35-
<label for="mail-return-char-select"><strong>Select Character:</strong></label>
36-
<select id="mail-return-char-select" class="form-select mb-3">
37-
<option value="">-- Select a character --</option>
38-
<?php foreach ($chars as $char) { ?>
39-
<option value="<?= intval($char["guid"]) ?>" data-name="<?= esc_attr($char["name"]) ?>">
40-
<?= esc_html($char["name"]) ?> (Level <?= intval($char["level"]) ?>)
41-
</option>
42-
<?php } ?>
43-
</select>
44-
45-
<div id="mail-return-loading" style="display:none;" class="text-center my-3">
46-
<div class="spinner-border text-primary" role="status">
47-
<span class="visually-hidden">Loading...</span>
36+
<script>const whTooltips = {colorLinks: true, iconizeLinks: true, renameLinks: true};</script>
37+
<div class="wrap" id="acore-mail-return-page">
38+
<div id="mail-return-layout">
39+
40+
<!-- Col 1: character selector - always visible, same width -->
41+
<div id="mail-return-sidebar">
42+
<div class="card">
43+
<div class="card-body">
44+
<h3>Mail Return</h3>
45+
<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>
46+
<hr>
47+
48+
<strong>Select Character:</strong>
49+
<ul id="acore-characters-mail" class="acore-char-list list-unstyled mt-2">
50+
<?php foreach ($chars as $char) {
51+
$clsStyle = AcoreCharColors::rowStyle(intval($char['class']), intval($char['race']));
52+
?>
53+
<li>
54+
<button type="button" class="acore-char-row acore-char-card" data-char-guid="<?= intval($char['guid']) ?>" style="<?= esc_attr($clsStyle) ?>">
55+
<span class="acore-char-name"><?= esc_html($char['name']) ?></span>
56+
<span class="acore-char-meta">
57+
<span class="acore-level" data-exp="<?= AcoreCharColors::expansionSlug(intval($char['level'])) ?>" title="<?= esc_attr(AcoreCharColors::expansionLabel(intval($char['level']))) ?>">Level <?= intval($char['level']) ?></span>
58+
<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' ?>">
59+
<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' ?>">
60+
</span>
61+
</button>
62+
</li>
63+
<?php } ?>
64+
</ul>
65+
66+
<div id="mail-return-loading" style="display:none;" class="text-center my-3">
67+
<div class="spinner-border text-primary" role="status">
68+
<span class="visually-hidden">Loading...</span>
69+
</div>
70+
</div>
71+
72+
<!-- Empty state lives inside the card -->
73+
<div id="mail-return-empty-wrap" style="display:none;">
74+
<hr>
75+
<h5>Unread Sent Mails</h5>
76+
<p id="mail-return-empty" class="text-muted">No unread sent mails found for this character.</p>
4877
</div>
4978
</div>
79+
</div>
80+
</div><!-- /mail-return-sidebar -->
5081

51-
<div id="mail-return-list" style="display:none;">
52-
<h5>Unread Sent Mails</h5>
53-
<ul id="mail-return-items" class="list-unstyled"></ul>
54-
<p id="mail-return-empty" style="display:none;" class="text-muted">No unread sent mails found for this character.</p>
82+
<!-- Cols 2-3: mail list - hidden until mails are loaded by JS -->
83+
<div id="mail-return-content">
84+
<div class="card">
85+
<div class="card-body">
86+
<h5 id="mail-return-heading">Unread Sent Mails</h5>
87+
<ul id="mail-return-items" class="list-unstyled mb-0"></ul>
5588
</div>
5689
</div>
57-
</div>
58-
</div>
59-
</div>
60-
61-
<script>
62-
var mailReturnData = {
63-
nonce: "<?php echo esc_js(wp_create_nonce('wp_rest')); ?>",
64-
mailsUrl: "<?php echo esc_url(get_rest_url(null, ACORE_SLUG . '/v1/mail-return/list')); ?>",
65-
returnUrl: "<?php echo esc_url(get_rest_url(null, ACORE_SLUG . '/v1/mail-return')); ?>",
66-
assetsUrl: "<?php echo esc_url(ACORE_URL_PLG . 'web/assets/'); ?>"
67-
};
68-
</script>
69-
<script>var whTooltips = {colorLinks: true, iconizeLinks: true, renameLinks: true};</script>
70-
<script src="https://wow.zamimg.com/js/tooltips.js"></script>
71-
<?php
90+
</div><!-- /mail-return-content -->
91+
92+
</div><!-- /mail-return-layout -->
93+
</div><!-- /wrap -->
94+
95+
<?php
7296
return ob_get_clean();
7397
}
7498
}

src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollMenu.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,19 @@ public static function I()
2424
return self::$instance;
2525
}
2626

27+
private function isAvailable(): bool
28+
{
29+
if (Opts::I()->acore_resurrection_scroll != '1') {
30+
return false;
31+
}
32+
$csv = get_option('acore_modules_csv', '');
33+
$modules = $csv ? array_map('trim', explode(',', $csv)) : [];
34+
return in_array('mod-resurrection-scroll', $modules);
35+
}
36+
2737
function acore_resurrection_scroll_menu()
2838
{
29-
if (Opts::I()->acore_resurrection_scroll == '1') {
39+
if ($this->isAvailable()) {
3040
add_submenu_page('profile.php', 'Scroll of Resurrection', 'Scroll of Resurrection', 'read', ACORE_SLUG . '-resurrection-scroll', array($this, 'acore_resurrection_scroll_menu_page'));
3141
}
3242
}
@@ -41,6 +51,5 @@ function acore_resurrection_scroll_menu_page()
4151
function resurrection_scroll_menu_init()
4252
{
4353
$menu = ResurrectionScrollMenu::I();
44-
4554
add_action('admin_menu', array($menu, 'acore_resurrection_scroll_menu'));
4655
}

0 commit comments

Comments
 (0)