Skip to content

[4 / 7] - Item Restoration#201

Open
TheSCREWEDSoftware wants to merge 4 commits into
azerothcore:masterfrom
TheSCREWEDSoftware:4_user_itemrestore
Open

[4 / 7] - Item Restoration#201
TheSCREWEDSoftware wants to merge 4 commits into
azerothcore:masterfrom
TheSCREWEDSoftware:4_user_itemrestore

Conversation

@TheSCREWEDSoftware

@TheSCREWEDSoftware TheSCREWEDSoftware commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Split from the main PR: #197

Split by Claude

Redesigned item-restoration page (cards, quality-coloured icons, tooltips) and hardening: ownership checks so a user can only restore their own characters' items, plus input validation. Carries the shared UserController/UserView the later branches need.

Realised after that this UI is what we show the users I think via the shop implementation

This is broken until the 6th PR

Current PR
image

6th PR #203
image

image

Summary by CodeRabbit

  • New Features

    • Dark mode toggle for admin panel
    • Character order reset functionality
    • Redesigned item restoration interface with character sidebar picker and item cards
    • Enhanced mail display showing item quality indicators and cash-on-delivery information
  • Bug Fixes

    • Added authorization validation for item restoration actions
  • Style

    • Updated admin interface styling and layout improvements across character and item management pages

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds AcoreCharColors, a shared PHP utility for WoW class/race/faction color and expansion metadata. Uses it to rebuild the Characters menu, Item Restoration page, and Mail Return page with sidebar character pickers and card-based layouts. Hardens ACoreServices and ToolsApi with ownership validation and REST permission guards. Introduces a persistent per-user admin dark mode toggle with AJAX, inline styles, and a new CSS theme system (dark-mode.css, theme.css).

Changes

Character Color Utility, Security, Feature UI Overhaul

Layer / File(s) Summary
AcoreCharColors utility and boot wiring
src/.../Utils/AcoreCharColors.php, src/.../boot.php
Adds AcoreCharColors with class/race/faction constant maps, static name, rowStyle, and expansion helpers; registered in boot.php.
ACoreServices ownership validation and ToolsApi guards
src/.../Manager/ACoreServices.php, src/.../Components/Tools/ToolsApi.php
getRestorableItemsByCharacter validates account ownership and expands returned columns; adds currentAccountOwnsCharacterName. ItemRestore adds positive-integer validation and ownership check returning WP_Error 400/403; REST routes gain is_user_logged_in permission callbacks.
Characters menu controller and view overhaul
src/.../CharactersMenu/CharactersController.php, src/.../CharactersMenu/CharactersView.php
loadHome gains nonce check and a resetCharacterOrder branch; new private resetCharacterOrder() NULLs order for the account. View renders AcoreCharColors-styled flex rows with nonce field, Save/Reset buttons, and an updated sortable JS update callback.
Item Restoration page PHP template and client-side logic
src/.../UserPanel/Pages/ItemRestorationPage.php
Template rebuilt with AcoreCharColors sidebar character picker and loading/empty-state containers. IIFE fetches items by GUID, renders quality-colored Wowhead item cards, handles restore with card removal and renumbering, polls for WowheadPower icon upgrades.
Mail Return controller, view, and JS overhaul
src/.../MailReturnMenu/MailReturnController.php, src/.../MailReturnMenu/MailReturnView.php, src/.../mail-return/mail-return.js
Controller adds cod and item_quality to SQL results. View switches character picker to AcoreCharColors sidebar and injects data via wp_localize_script. JS replaces select handler with card click, rebuilds mail DOM with quality borders, level badges, CoD labels, and adds upgradeWowheadIcons polling.
main.css: unified character list, mail return, and item restore layouts
src/.../web/assets/css/main.css
Replaces legacy order/unstuck CSS with .acore-char-list / button.acore-char-row flex system. Adds grid layouts and card styling for mail return and item restoration pages plus profile subpage containers.
theme.css: color palette, item cards, 2FA, security, and myCRED theming
src/.../web/assets/css/theme.css
New file: establishes light/dark CSS custom property palettes and item-quality color variables; styles item restoration cards, Security/Mail Return/2FA/myCRED pages, WP 2FA wizard and micromodal, connection table, and button/form theming.

Admin Dark Mode Toggle

Layer / File(s) Summary
DarkMode PHP hook: admin bar, AJAX, enqueue, and WP2FA wizard
src/.../Hooks/Various/DarkMode.php, src/.../boot.php
Registers admin-bar toggle node, admin_body_class filter, asset enqueue, admin_head late inline styles, AJAX toggle handler, inline JS generator, and WP 2FA wizard CSS hook. Loaded in boot.php.
dark-mode.css: global dark theme overrides
src/.../web/assets/css/dark-mode.css
Full dark-mode stylesheet: Bootstrap variable reset under body.acore-dark-mode, page-specific rules for profile/security/item-restore/mail return, character rows, form controls, tables, buttons, myCred widgets, expansion badges, and admin-bar icon.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(100, 149, 237, 0.5)
    Note over Browser, ToolsApi: Item Restoration Flow
  end
  participant Browser
  participant ItemRestorationPage as ItemRestorationPage.php (IIFE)
  participant ToolsApi
  participant ACoreServices
  participant DB as characters / recovery_item

  Browser->>ItemRestorationPage: click .acore-char-row (guid, cname)
  ItemRestorationPage->>ToolsApi: GET /item-restore/list/{cguid}
  ToolsApi->>ACoreServices: getRestorableItemsByCharacter(guid)
  ACoreServices->>DB: verify account owns non-deleted char
  DB-->>ACoreServices: row or empty
  ACoreServices->>DB: SELECT Id, ItemEntry, Count, DeleteDate FROM recovery_item
  DB-->>ACoreServices: item rows
  ACoreServices-->>ToolsApi: items[]
  ToolsApi-->>ItemRestorationPage: JSON items[]
  ItemRestorationPage->>Browser: render item cards + applyWowheadIcons()

  Browser->>ItemRestorationPage: click Restore button (item, cname)
  ItemRestorationPage->>ToolsApi: POST /item-restore {item, cname}
  ToolsApi->>ACoreServices: currentAccountOwnsCharacterName(cname)
  ACoreServices-->>ToolsApi: bool
  ToolsApi->>ToolsApi: SOAP item restore $item $cname
  ToolsApi-->>ItemRestorationPage: success
  ItemRestorationPage->>Browser: remove card, renumber, show empty-state if none remain
Loading
sequenceDiagram
  rect rgba(144, 238, 144, 0.5)
    Note over AdminBar, UserMeta: Dark Mode Toggle Flow
  end
  participant AdminBar as WP Admin Bar
  participant JS as acore_dark_mode_js
  participant AJAX as wp_ajax_acore_toggle_dark_mode
  participant UserMeta as acore_dark_mode user meta

  AdminBar->>JS: click `#acore-dark-mode` icon link
  JS->>AJAX: POST admin-ajax.php {action, nonce}
  AJAX->>AJAX: check_ajax_referer(acore_dark_mode)
  AJAX->>UserMeta: get current value → toggle 0↔1
  AJAX->>UserMeta: update_user_meta
  AJAX-->>JS: {success: true, data: {dark: bool}}
  JS->>JS: toggle body.acore-dark-mode class
  JS->>AdminBar: swap icon HTML + update link title
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐇 Hop hop, the darkness yields to flair,
New character cards float bright in air,
A nonce here, a guard over there —
Items restored with faction flair!
The rabbit approves this stylish lair.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title '[4 / 7] - Item Restoration' is partially related to the changeset; it references the item restoration feature but omits significant changes across characters menu, mail return, dark mode, and shared components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🧹 Nitpick comments (1)
src/acore-wp-plugin/src/Components/UserPanel/Pages/ItemRestorationPage.php (1)

217-233: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Success detection is overly coupled to message text.

parseJsonResponse already enforces HTTP success, but this branch treats success only when a string contains "mail". That is brittle against structured responses or wording changes.

Suggested refactor
             .then(function (data) {
-                if (typeof data === 'string' && data.toLowerCase().includes('mail')) {
+                var message = typeof data === 'string'
+                    ? data
+                    : (data && data.message ? data.message : 'Item restored successfully.');
+                if (!data || !data.error) {
                     cardEl.parentElement.removeChild(cardEl);
-                    successBox.textContent = data;
+                    successBox.textContent = message;
                     successBox.classList.remove('invisible');
                     ...
                 } else {
-                    errorBox.textContent = typeof data === 'string' ? data : JSON.stringify(data);
+                    errorBox.textContent = typeof data === 'string' ? data : JSON.stringify(data);
                     btnEl.disabled = false;
                     btnEl.textContent = 'Restore';
                 }
             })
🤖 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/UserPanel/Pages/ItemRestorationPage.php`
around lines 217 - 233, The success detection is tightly coupled to the response
message containing the word "mail", which is brittle and fragile to message
wording changes. Since parseJsonResponse already enforces HTTP success status,
remove the brittle string content check (the includes('mail') condition) and
instead trust that if the response reaches this point without throwing an error,
it is successful. Refactor the conditional logic so that the success path
(removing the card, displaying the success box, renumbering remaining cards, and
checking if no results remain) is executed whenever the HTTP response is
successful, regardless of the specific message content, and only execute the
error path if parseJsonResponse throws an exception or returns an error status.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php`:
- Around line 48-49: The race-icon and class-icon img tags are missing alt
attributes, which are critical for accessibility as screen readers rely on alt
text to describe images. Add an alt attribute to both img elements: for the
race-icon, use the race name from
AcoreCharColors::getRaceName(intval($char["race"])) and for the class-icon, use
the class name from AcoreCharColors::getClassName(intval($char["class"])).
Ensure the alt text is wrapped with esc_attr() for proper escaping, similar to
how the title attributes are currently handled.

In `@src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnView.php`:
- Around line 44-49: User-facing strings in the MailReturnView.php file are
hardcoded in English without translation function wrappers, preventing
localization. Wrap all hardcoded text strings like "Mail Return", the
instruction paragraph "You can return sent mails...", "Select Character:", and
any other similar strings mentioned at lines 75-77 in the appropriate WordPress
translation function (such as __() for returning translated strings or _e() for
echoing them directly) using the plugin's text domain. Ensure consistency with
how other user-facing strings are handled throughout the rest of the WP UI in
this plugin.

In `@src/acore-wp-plugin/src/Components/Tools/ToolsApi.php`:
- Around line 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.
- Around line 14-22: The current authorization only verifies that the user owns
the character name via currentAccountOwnsCharacterName, but does not verify that
the recovery item ID ($item) actually belongs to that character. This creates a
security vulnerability where a user could submit another user's recovery item ID
with their own character name. Add a new authorization check using a service
helper method (similar to getOwnedRestorableItemCharacterName) that joins the
recovery_item table with the characters table to verify both the item ID and
character name are owned together by the current user, then use this method to
authorize the request before calling executeCommand with the item restore
command.

In `@src/acore-wp-plugin/src/Components/UserPanel/Pages/ItemRestorationPage.php`:
- Around line 34-35: The race-icon and class-icon img elements on this page are
missing alt attributes required for accessibility. Add alt attributes to both
img elements that display the same descriptive text as their title attributes;
for the race-icon use AcoreCharColors::getRaceName() with the race value, and
for the class-icon use AcoreCharColors::getClassName() with the class value.
Ensure both alt attributes are properly escaped using esc_attr() for consistency
with the existing title attributes.
- Around line 199-202: The catch block handling fetch failures sets the error
message in errorBox but does not make it visible to the user since the
containing element remains hidden. In the catch function that currently hides
the loading element and sets errorBox.textContent, also explicitly display the
errorBox or its parent container by setting the appropriate CSS display property
to make the error message visible when a fetch failure occurs.
- Around line 22-35: The ItemRestorationPage template is accessing character
array keys (guid, name, class, race, level, gender) that are not guaranteed to
be present in the upstream data source, causing undefined key warnings. Rather
than fixing this in the template, locate the character query or data provider
that populates the $characters array passed to this template and ensure it
always includes all required keys (guid, name, class, race, level, gender) in
the result set. This ensures the contract between the data source and the
template is fulfilled at the point of data retrieval, not masked in the view
layer.
- Around line 114-203: The selectCharacter function has a race condition where
multiple in-flight fetch requests can complete out of order, causing an older
response to overwrite newer data in the grid. To fix this, implement a request
token mechanism by creating a unique identifier that gets incremented or updated
each time selectCharacter is called, capturing this token in the fetch promise
chain, and then checking if the token is still current before rendering the
items in the response handler. Only proceed with rendering the grid if the
stored token matches the current request token; otherwise, discard the stale
results to prevent outdated character items from being displayed.

In `@src/acore-wp-plugin/src/Hooks/Various/DarkMode.php`:
- Around line 121-129: The dark mode toggle click handler does not prevent
concurrent AJAX requests, which causes multiple overlapping toggle operations
when users click rapidly. Fix this by introducing a flag variable (e.g.,
isToggling) that tracks whether a request is currently in progress. Before
making the $.post request in the click handler on '`#wp-admin-bar-acore-dark-mode`
> a', check if isToggling is true and return early if it is. Set isToggling to
true before the request and set it back to false in the callback after the
response is processed, ensuring only one toggle operation can occur at a time.

In `@src/acore-wp-plugin/src/Utils/AcoreCharColors.php`:
- Around line 86-92: The current ternary logic in the faction color assignment
treats any non-Alliance value (including the 'unknown' default) as Horde,
causing invalid or missing race IDs to render with Horde colors. Replace the
simple ternary operators for $fLight with explicit branching using
if/elseif/else statements to handle three distinct cases: when $faction equals
'alliance', when $faction equals 'horde', and a fallback neutral color for the
'unknown' case. Also remove the unused $fDark variable which duplicates the
$fLight logic, and ensure the sprintf call uses the correctly assigned faction
color variable.

In `@src/acore-wp-plugin/web/assets/css/main.css`:
- Around line 364-368: The `#mail-return-items` grid is using a fixed two-column
layout with grid-template-columns: 1fr 1fr, which forces items to stay in two
columns and causes overflow in narrow layouts. Replace the fixed two-column
setup with a responsive grid layout using auto-fit or auto-fill with minmax to
allow mail cards to wrap naturally based on available width. Apply the same
responsive approach to the .mail-items-grid rule mentioned in lines 473-484 so
that item slots also adapt to their container width instead of always reserving
space for six 56px slots.
- Around line 690-695: The `.item-restore-grid` class uses a fixed 4-column
layout that doesn't respect the actual available space, causing cards to shrink
below acceptable dimensions when the content area narrows. Replace the fixed
`grid-template-columns: repeat(4, 1fr)` with a responsive approach using
`auto-fit` or `auto-fill` combined with `minmax()` function to set a minimum
width for each card (approximately 80px for the icon plus padding), allowing
cards to wrap to the next row naturally when space is constrained rather than
squeezing horizontally.

In `@src/acore-wp-plugin/web/assets/mail-return/mail-return.js`:
- Around line 2-18: There is a race condition where overlapping AJAX requests
can cause stale mail data to be rendered if an older request completes after a
newer one. In the click handler for .acore-char-card, you need to abort any
previous AJAX request before initiating a new one (or alternatively store the
current charGuid as a request ID and validate responses match the current
selection). Store a reference to the active AJAX request (likely made in the
lines around 25-33 where the actual data fetch occurs) and call .abort() on any
existing request before making a new one, or add a guard in the response handler
to ensure the returned charGuid still matches the currently selected character
before rendering the mailItems.

---

Nitpick comments:
In `@src/acore-wp-plugin/src/Components/UserPanel/Pages/ItemRestorationPage.php`:
- Around line 217-233: The success detection is tightly coupled to the response
message containing the word "mail", which is brittle and fragile to message
wording changes. Since parseJsonResponse already enforces HTTP success status,
remove the brittle string content check (the includes('mail') condition) and
instead trust that if the response reaches this point without throwing an error,
it is successful. Refactor the conditional logic so that the success path
(removing the card, displaying the success box, renumbering remaining cards, and
checking if no results remain) is executed whenever the HTTP response is
successful, regardless of the specific message content, and only execute the
error path if parseJsonResponse throws an exception or returns an error status.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 119c3e4d-b692-43bb-b91a-45504a752e16

📥 Commits

Reviewing files that changed from the base of the PR and between 94af292 and 6f4f17b.

📒 Files selected for processing (14)
  • src/acore-wp-plugin/src/Components/CharactersMenu/CharactersController.php
  • src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php
  • src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnController.php
  • src/acore-wp-plugin/src/Components/MailReturnMenu/MailReturnView.php
  • src/acore-wp-plugin/src/Components/Tools/ToolsApi.php
  • src/acore-wp-plugin/src/Components/UserPanel/Pages/ItemRestorationPage.php
  • src/acore-wp-plugin/src/Hooks/Various/DarkMode.php
  • src/acore-wp-plugin/src/Manager/ACoreServices.php
  • src/acore-wp-plugin/src/Utils/AcoreCharColors.php
  • src/acore-wp-plugin/src/boot.php
  • src/acore-wp-plugin/web/assets/css/dark-mode.css
  • src/acore-wp-plugin/web/assets/css/main.css
  • src/acore-wp-plugin/web/assets/css/theme.css
  • src/acore-wp-plugin/web/assets/mail-return/mail-return.js

Comment thread src/acore-wp-plugin/src/Components/Tools/ToolsApi.php
Comment on lines +30 to 31
'callback' => function( $request ) {
return ToolsApi::ItemRestoreList($request);

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.

Comment thread src/acore-wp-plugin/src/Hooks/Various/DarkMode.php
Comment thread src/acore-wp-plugin/src/Utils/AcoreCharColors.php
Comment thread src/acore-wp-plugin/web/assets/css/main.css
Comment thread src/acore-wp-plugin/web/assets/css/main.css
Comment thread src/acore-wp-plugin/web/assets/mail-return/mail-return.js
TheSCREWEDSoftware added a commit to TheSCREWEDSoftware/acore-cms that referenced this pull request Jun 24, 2026
(Resume generated by Claude)

**PR azerothcore#199 — Characters Menu**
- Fixed typo: "succesfully" → "successfully" in save confirmation
- Fixed broken sentence in the Order Character Screen description
- Extended `user-select: none` to cover all `.acore-char-row` elements, not just `button`

**PR azerothcore#200 — Mail Return**
- *(issues covered by later PRs — no unique fixes)*

**PR azerothcore#201 — Item Restoration**
- Added `alt=` attributes to race/class icons in `CharactersView.php`
- `ToolsApi`: `ItemRestoreList` now catches exceptions and returns a 403 instead of a 500
- `ToolsApi` + `ACoreServices`: **Security fix** — `ItemRestore` now verifies the recovery item belongs to the character, not just that the user owns the character name (IDOR vulnerability)

**PR azerothcore#202 — Unstuck / Scroll / RAF**
- `ItemRestorationPage`: removed brittle `data.includes('mail')` success check — any HTTP success now triggers the success path

**PR azerothcore#203 — Security Tab**
- `CharactersController`: `resetCharacterOrder()` now guards against invalid/null account ID before running UPDATE
- `Tools.php`: added `esc_attr()` on banned-names table input (XSS fix)
- `Tools.php`: added `is_array()` guard on thresholds `foreach`
- `Tools.php` + `SettingsController`: added sentinel input so deleting all thresholds actually clears them in the DB
- `Tools.php`: GeoIP hidden fallback `value="0"` is now always rendered, not conditionally

**PR azerothcore#204 — Admin Settings / CSRF**
- `AcoreCharColors`: removed unused `$fDark`, unknown race IDs now get a neutral color instead of Horde red
- `MailReturnView`: added `esc_url()` on race/class icon `src` attributes
- `DarkMode`: added `dmToggleInFlight` flag to prevent overlapping AJAX requests on rapid dark mode clicks
@TheSCREWEDSoftware

Copy link
Copy Markdown
Contributor Author

The reviews are valid for this, but the are fixed in the on the way to latest PR and including he last PR #205

PR #201 — Item Restoration

  • Added alt= attributes to race/class icons in CharactersView.php
  • ToolsApi: ItemRestoreList now catches exceptions and returns a 403 instead of a 500
  • ToolsApi + ACoreServices: Security fixItemRestore now verifies the recovery item belongs to the character, not just that the user owns the character name (IDOR vulnerability)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant