Refactor: Playersearch standardization — active/inactive/banned tiers, full surface conversion, Advanced Search#477
Open
baltinerdist wants to merge 34 commits into
Open
Conversation
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Adds SearchService::RankedPlayers() with concentric-ring ranking (park->kingdom->global), abbreviation-prefix support via resolveAbbrevPrefix(), and SOAP registration for the session-less Search/Players action. 12-assertion curl ranking test. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
… SearchService::RankedPlayers Adds Controller_SearchAjax::players() as a same-origin, session-gated endpoint that delegates entirely to the existing SearchService::RankedPlayers() core — no SQL duplication. Returns [] for unauthenticated callers. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…k 3) New JS + CSS files implementing the one canonical player-search dropdown used by all search surfaces. Custom dropdown (no jQuery UI), position:fixed for modal safety, dark-mode via html[data-theme="dark"], &q= URL param, 220 ms debounce, keyboard navigation, and onSelect callback. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Add CSS and JS includes for the new OrkPlayerSearch component to the master theme file, making it available app-wide. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ilot) Replaces both jQuery UI .autocomplete() player-search blocks with OrkPlayerSearch.attach(); removes unused preloadedGivenByOfficers array. GivenBy is global (cross-kingdom, includeInactive+Suspended); GivenTo is kingdom-centered, active-only. Establishes the conversion recipe for the rollout. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…nent Replace jQuery UI autocomplete on #PlayerName in Attendance_kingdom, Attendance_park, and Attendance_event with OrkPlayerSearch.attach — single ranked server-side call (park→kingdom→global), no more 3-parallel-request hack in Attendance_park. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Replace three bespoke autocomplete implementations (edit-suspension "Suspended By", suspend-player "Player", suspend-player "Suspended By") with OrkPlayerSearch.attach. Park-scoped player search re-attaches on park dropdown change to preserve park filtering. Removes ~120 lines of duplicated fetch/render/keynav wiring and the three bespoke result divs. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ponent Convert #GivenBy (Admin_player), #PlayerName (Admin_suspendplayer, Admin_banplayer, Admin_moveplayer) and #PlayerName + #CreatePlayerName (Admin_manageevent) from jQuery UI .autocomplete() to OrkPlayerSearch.attach(). Uses ranked SearchAjax/players route; PenaltyBox radio logic updated to use p.Suspended from RankedPlayers response. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…h (superset of parallel + concurrent work) Converts 8 clean-path Xnew award/recommendation searches to OrkPlayerSearch.attach (pn-award-givenby, pn-edit-givenby, kn-award-player/givenby/rec, pk-award-player/givenby/rec), preserving officer-chip clearing and awardranks-on-select. Move/merge/event/reconcile deferred to the exception register. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…dditive rules) Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Additive, backward-compatible: excludeIds (array or per-search fn); setOpts()/reattach() for live re-scoping (move-player cascades); preload list on empty-focus. Existing attach() unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…:RankedPlayers KingdomAjax/ParkAjax/EventAjax/AdminAjax playersearch + SearchAjax::universal player branch delegate to the shared ranking core (consistent park->kingdom->everywhere). Adds excludeKingdomId/excludeParkId for scope=exclude. Response shapes preserved; Search/Player SOAP untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Converts 5 bespoke exception surfaces in revised.js to the standard OrkPlayerSearch component, adding excludeKingdomId/excludeParkId support to SearchAjax/players and the OrkPlayerSearch component along the way. - kn-moveplayer: attach+reattach on mode change; 'in'→excludeKingdomId, 'within'/'out'→restrictTo:'kingdom' - pk-moveplayer: attach+reattach on mode change; 'in'→excludeParkId, 'out'→restrictTo:'park' - kn-merge (keep+remove): attach with cross-field excludeIds fn - pk-merge (keep+remove): attach with restrictTo:'park' + cross-field excludeIds fn - ev-PlayerName: attach with parkId+kingdomId ranking + dynamic excludeIds from attended rows; includeInactive+includeSuspended preserved Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…h component Replaces legacy cpAc()+cpSearchPlayersGlobal() calls in Admin_index.tpl with OrkPlayerSearch.attach() for the Move Player and Merge Players modals. Merge Players uses excludeIds lambdas so each field suppresses the player already chosen in the other field. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…omponent
Replace both jQuery UI autocomplete calls in Admin_unit.tpl with
OrkPlayerSearch.attach — global, unscoped (no parkId/kingdomId/restrictTo),
includeInactive:true per the documented cross-kingdom/cross-park exception.
Also removes the stale dead `park_id = $('#ParkId').val()` assignment that
was never passed to the old AJAX call.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
… search to OrkPlayerSearch Replaces the hand-rolled kn-ac fetch/render/keynav wiring in both permission templates with OrkPlayerSearch.attach, using appropriate scope: kingdom-scoped or park-scoped for the scoped page (determined at runtime from $type), and global (no restrictTo) for the global admin page. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…th cross-field excludeIds Replaces both #FromPlayerName and #ToPlayerName jQuery UI .autocomplete() calls with OrkPlayerSearch.attach(). Each field passes excludeIds: function() returning the other field's currently-selected MundaneId, preventing a player from being merged into themselves. Global scope (no center/restrictTo), includeInactive: true. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…nent Replace bespoke acSearch-based givenby autocomplete in wireRow() with OrkPlayerSearch.attach(), keyed to RcConfig.kingdomId for ranking with global scope (no restrictTo), includeInactive+includeSuspended for historical attribution. Location search left untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…yerSearch Last straggler jQuery-UI player autocomplete (#Suspendator), kingdom-scoped to the recording officer's kingdom. Completes the player-search standardization. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…Clear, a11y) + backend fixes Component: AbortController cancels stale in-flight searches; module-level shared click/scroll listeners (no per-attach leak); new onClear callback fired on edit so callers clear stale hidden ids; live uir read; ArrowUp guard; dark-mode badge contrast. Backend: AdminAjax/global passes session token (admins can find restricted-name players); Search/Players SOAP registration gains excludeKingdomId/excludeParkId; Reports_roster sp-player search uses reattach so park-change re-scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ale hidden id on re-edit Converted templates dropped the old jQuery-UI change-handler that cleared the hidden MundaneId when a user edited the text after selecting. Wire onClear on every attach/reattach so the hidden id (and submit-enable state) is cleared the moment the user modifies the input, eliminating stale-id form submissions. Also replaces the redundant manual input-event listeners in Admin_permissions.tpl and Admin_permissions_global.tpl with the canonical onClear callback. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…arch.attach call sites Adds onClear callback to all 15 OrkPlayerSearch.attach calls in revised.js. Without it, editing the player name field after a selection left a stale hidden id, causing the wrong player to be submitted. Award givenby surfaces also deselect officer chips on clear, matching their onSelect behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
After selecting a player and editing the text, the hidden id stayed stale. Adds onClear to cp-mp-player-name (Move Player), cp-mgp-keep-name and cp-mgp-remove-name (Merge Players) in Admin_index.tpl, and to the wireRow givenby attach in Playernew_reconcile.tpl, each mirroring its onSelect minus the id-set so the submit guard re-evaluates correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ehind modals) Modal overlays use z-index up to 999999; the .ops-ac-results dropdown was 10000, so inside any modal (Xnew award/rec/move/merge, Admin modals) the ranked results rendered BEHIND the modal and appeared as zero results. Raised to 2147483000. Non-modal pages were unaffected. Found via in-browser visual testing (headless DOM reads saw the items but not the occlusion). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…(was static)
Conversion baked a static $kid/$pid; the old code read $('#KingdomId').val() live so the
player search followed the kingdom/park selector. Restored: init scope from the live hidden
fields and reattach() on #KingdomName/#ParkName select/change so ranking tracks the chosen
scope. (Attendance_event keeps the event's fixed location.) Found via in-browser testing.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…rs autopopulate; convert Edit Officers search to playersearch
Officer data uses lowercase roles (monarch/regent/prime_minister/gmr) but code matched
title-case ('Monarch'/'Regent'):
- controller.Kingdom/Park: $preloadOfficers built nothing → award 'Given By' quick-buttons
(kingdom/park monarch+regent) never rendered. Now case-insensitive.
- revised.js Edit Officers: officerMap keyed/looked-up by title-case → no autopopulate, Vacate
hidden. Now normalized (normRole) so current holders prefill + Vacate shows.
- Edit Officers per-role search was a MISSED conversion (jQuery UI Search/Player) → now
OrkPlayerSearch (kingdom-scoped), modal style unchanged.
Pre-existing role-case bug; found via in-browser testing.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…nversion as Kingdom Mirror of the Kingdom Edit Officers fix for the park officer editor (normRole autopopulate so current holders prefill + Vacate shows; per-role search converted from jQuery UI to OrkPlayerSearch, kept kingdom-wide per the existing comment). Verified: 5 park holders autopopulate with Vacate. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ero monarch/regent display Same lowercase-role mismatch as Kingdom/Park: $preloadOfficers (award Given By quick-chips) on Playernew/Admin/legacy-Award pages never populated, and Kingdomnew/Parknew hero monarch/regent extraction never matched. All officer-role comparisons now case-insensitive; chip Role labels prettified (e.g. 'Kingdom Monarch'). Verified: Playernew add-award shows 4 chips (kingdom + park monarch/regent). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
83bb92b to
fcb5bdc
Compare
…SQLi fix
Rework SearchService::RankedPlayers into a three-tier model (active → inactive
last-resort → banned) behind every OrkPlayerSearch surface:
- Inactive players always included, ranked after all active (last resort).
- Banned (suspended OR penalty_box) shown ONLY to viewers with CREATE/EDIT
authority relevant to the surface, scoped ONE LEVEL UP: a park surface
surfaces the kingdom's banned players, a kingdom/global surface surfaces
Amtgard-wide. Plain users never see banned.
- restrictTo:'kingdom' is now principality-family-aware (centralized).
- Added restrictKingdomIds (fixes KingdomAjax scope=own silent-drop bug),
excludeIds, offset, and a stable mundane_id paging tiebreak.
- SECURITY: mysql_real_escape_string is a no-op polyfill here — replaced every
player/term LIKE interpolation in RankedPlayers + legacy Player() + the
Event/Kingdom/Park name searches with a real likeEscape().
- Pure-numeric query is a direct mundane_id lookup (paste a copied id).
Endpoint: SearchAjax/players now returns {rows,hasMore,offset} (limit+1 probe)
and accepts the full surface context; the 5 other RankedPlayers callers convert
to the options array. Component: Load more pagination, server-side excludeIds,
context opts, ARIA combobox + live region, distinct Banned/Suspended/Inactive
badges, robustness (abort-on-close, error/loading rows, resize reposition,
blur-close, scrollIntoView, flip-up), and refresh() for scope-toggle re-query.
Adds tests/playersearch/banned_matrix.sh (authenticated full matrix, 15/15).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…+ fixes Convert every remaining hand-rolled 3.5.x player search to the shared component: - Parknew "Enter Attendance" modal (scope toggle park/kingdom/global via reattach+refresh, already-entered excludeIds, inactive-reactivation confirm and last-class prefill preserved). - Kingdom/Park "Move Player" (mode "in" → excludeKingdomId/excludeParkId, "within"/"out" → restrictTo) and Admin control-panel Move Player (cascade = hard kingdom/park filter). - Unit add member/manager (stays GLOBAL per the documented exception; excludeIds = current members). Surface bug fixes: - Award GivenAt handler no longer wipes the recipient MundaneId. - Reports_roster Edit-Suspension "Suspended By" passes the correct entity (park id on a park-scoped roster, not as kingdomId). - Admin Move Player SrcPark select re-scopes the player search. - Attendance_event re-scopes when the kingdom/park selector changes. - Kingdom merge gains restrictTo:'kingdom' (parity with park merge). Dead code removed: #PlayerSearch jQuery-UI bindings in default.js + orkui.js, and the superseded EventAjax auth/playersearch + AdminAjax global/playersearch action branches. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
New full-page rich player search (route Search/advanced, opens in a new tab from
the inline component's split "…or Advanced Search" footer, carrying the typed
term + surface scope). Officer/admin tool styled with the Reports look.
- Backend SearchService::AdvancedPlayers(): same tier/auth model as RankedPlayers
plus home + last-attendance (kingdom/park/date) columns and gated real names
(officers see them; a "restricted" player's real name shows only to global
admins). Filters: Active/Inactive toggles, officer-gated Banned (Amtgard-wide),
kingdom→park cascade, optional last-attendance date range. needFilter guard
prevents a full-table scan.
- Page: jQuery DataTable (responsive — no horizontal scrollbar on desktop,
collapses on mobile) with zebra striping, pagination, "Show N rows" /
"Showing X–Y of N", sortable headers; shortened kingdom names
("The Kingdom of the Wetlands" → "Wetlands"); per-row link-out + copy-ID
button (instant CSS tooltips, never native title); dark-mode throughout.
Fixes a theme-wide duplicate-scrollbar (html,body height:100% + overflow-x
forcing body into its own scroll container), page-scoped.
Adds tests/playersearch/chrome-matrix-protocol.md (full in-Chrome test matrix).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…overflow-y:auto)
The mobile-nav width clamp set `html, body { overflow-x: hidden }`, which per the
CSS spec forces `overflow-y` to compute as `auto`. Combined with `height: 100%`,
that turned <body> into its own vertical scroll container on top of the window —
a duplicate scrollbar on every tall page.
Switch to `overflow-x: clip`: it still clamps horizontal overflow (preserving the
Android fixed-bar fix) but does NOT establish a scroll container or couple
overflow-y, so the body no longer scrolls and only the viewport's single scrollbar
remains. Verified across Home, Kingdom, and Advanced Search (light + dark).
Removes the now-redundant page-scoped html/body override added to Search_advanced.tpl.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Replaces ~40 divergent player-search implementations (6 incompatible backend endpoints, jQuery-UI + bespoke dropdowns) with one standardized player search, then layers on a consistent active → inactive → banned result model and a rich Advanced Search page.
One core, one endpoint, one component
SearchService::RankedPlayers(): concentric-ring ranking centered on the surface (Park → Kingdom → Everywhere), rank-don't-exclude by default, opt-inrestrictTo/restrictKingdomIds/excludeKingdomId/excludeParkId/excludeIds. Kills the recurring "can't find people outside my kingdom" bug class.SearchAjax/players(+ session-less SOAPSearch/Playersfor testing), returning an envelope{rows, hasMore, offset}. The 6 legacy endpoints (KingdomAjax/ParkAjax/SearchAjax-universal/AdminAjax/EventAjaxplayersearch +SearchService::Player) delegate to the core so ranking can't drift again.OrkPlayerSearch(custom dropdown, never jQuery UI; fixed-position for modals; dark-mode; ARIA combobox;excludeIds/reattach/preload/onClear), loaded app-wide.Active / inactive / banned result model (new)
Every surface now returns three tiers in one consistent order:
suspendedORpenalty_box) shown only to viewers with CREATE/EDIT authority relevant to the surface, and scoped one level up: a park surface surfaces the kingdom's banned players; a kingdom / global / universal surface surfaces Amtgard-wide bans. Plain users never see banned players anywhere.restrictTo:'kingdom'is now principality-family-aware (a "within kingdom" / kingdom-merge search still finds principality members).Advanced Search page (new)
A full-page power-search at
Search/advanced, opened in a new tab from the inline component's splitLoad more… | …or Advanced Searchfooter (carrying the typed term + current scope):restrictedplayer's real name is admin-only), home kingdom/park, last-attendance kingdom/park + date, status pill, link-out, and a copy-Player-ID button (instant CSS tooltip "Copy Player ID - Paste this into your search!").mundane_idlookup — copy an id here, paste it into any player search.Surfaces converted (~40 call sites)
Award giver/recipient, attendance (kingdom / park / event + the Parknew sign-in modal with its scope toggle, already-attended exclusion, and inactive-reactivation confirm), recommendations, Move Player (Kingdom / Park / Admin — mode-driven exclude/restrict + cascade re-scope), Merge (cross-field exclude; kingdom merge now
restrictTo:'kingdom'), Unit add member/manager (intentionally global), officer / permission grants (scoped + global), reconcile, Edit Officers (Kingdom + Park), and the universal header search.Security
mysql_real_escape_stringis a no-op polyfill in this codebase, so player/termLIKEinterpolation was unescaped (SQL-injectable). Replaced every term interpolation inRankedPlayers, the legacyPlayer(), and the Event/Kingdom/Park name searches with a reallikeEscape()(quote-doubling +% _ \escaping). Banned-visibility and restricted-real-name gating are enforced server-side from the session token — client params can't widen them.Surface bug fixes & cleanup
#PlayerSearchjQuery-UI bindings and the supersededEventAjax/AdminAjaxplayersearch endpoints.Testing
tests/playersearch/banned_matrix.sh— authenticated full matrix (active/inactive/banned × park/kingdom/global × anon/plain/officer/admin), 15/15 green;tests/playersearch/ranking.sh12/12.tests/playersearch/chrome-matrix-protocol.md— full in-Chrome test protocol.Notes for review
KingdomAjax/ParkAjaxplayersearchendpoints were left in place (harmless) now that move-player routes throughSearchAjax/players.🤖 Generated with Claude Code