Repository navigation
Menu builder and page builder (reworked, with XSS and permission fixes) - #21
Merged
Merged
Conversation
Admin -> Menu builder builds the admin sidebar (sections and links up to three
levels, drag or arrow buttons, per-item permission); Admin -> Pages builds public
pages from blocks served at /page/{address}.
Fixes over the code this starts from: link and image addresses are validated
(no javascript:/data:/protocol-relative), the sidebar escapes them and supports
external links; the text block is rebuilt from a DOM allow-list instead of
strip_tags (which kept every attribute); relative addresses resolve against the
site, not /page/; raw HTML needs its own pages.html permission that is not in the
admin role or the '*' default; menu parents are validated (exists, is a link, no
loops, depth); the list is a server-side DataTable; permissions for existing admin
roles are granted by a migration; colour fields no longer default to white
(white text on white CTA). Also fixes data-initial-order clashing with
DataTables' own data-order and data-confirm on non-submit buttons.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYbe92Bns1mKdy23b7txT8
- A new "dropdown" kind: no address of its own, it opens the links put inside it (and is hidden while empty); links and dropdowns can be parents, sections cannot. The form shows only the fields that matter for the chosen kind (the old inline script is replaced by assets/js/menu-form.js, which the CSP-nonce page no longer needs). - Icon picker (assets/js/icon-picker.js): a searchable grid of all 2,050 Bootstrap Icons (list in vendor/bootstrap-icons/icons.json, drawn in chunks of 150), live preview, "no icon", full screen on phones. Used by the menu form and the module generator's icon field. - Fix: links to pages of this site in the sidebar opened in a new tab, because the resolved (absolute) address was mistaken for an external one. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYbe92Bns1mKdy23b7txT8
…r the account ones The menus and pages migrations (20261006/07) were older than the account-security migration (20261008) that is now on main. CodeIgniter only runs migrations newer than the current version, so a database that had already migrated to 20261008 would have skipped them and never got the menus and pages tables. They are renumbered to 20261009/10 (the permissions grant to 20261011) so every database creates them. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYbe92Bns1mKdy23b7txT8
godzam
marked this pull request as ready for review
November 1, 2026 05:39
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.
Summary
Starts from the menu/page builder code on the
freebuff/…branch (the tables, models, controllers and editor UI are kept) and fixes what was wrong in it. Independent of the account-features PR (#20).What the builders do
admin/menus): sections and links for the admin sidebar, nested up to three levels, drag or arrow buttons, optional permission / super-admin-only per item, merged after the items inmenu.php.admin/pages): public pages from blocks (hero, text, features, CTA, image, custom HTML) at/page/{address}; drafts are previews for editors.Fixed over the original
javascript:became a clickable link, and the sidebar did not escape the URL (an attribute-breaking"was possible). NowApp\Page\Linksallows only http(s)/mailto/tel and this site's pages; the sidebar escapes and handles external links (rel="noopener noreferrer").strip_tagswith an allow-list, which keeps every attribute (<a onmouseover=…>,<a href="javascript:…">). Replaced by a DOM allow-list (App\Page\Html): no attributes except a safehref; scripts/styles/svg/math/forms removed; 22 hostile payloads in the tests./page/…(aregisterbutton went to/page/register); they now go throughsite_url.pages.html, which is not in theadminrole (fresh installs included:not_in_wildcard), and an editor without it can keep existing HTML blocks but not add/change one.parent_idwas not validated (missing parent, a section, a loop, unlimited depth); now it is (MenuTree::canNest, found and fixed a bug for new items), and the form shows the errors.admin/pages/data).data-orderof our DataTables helper clashed with DataTables' own attribute (any table using theorderoption crashed), anddata-confirmon a non-submit button threw inrequestSubmit.Test plan
PageSafetyTest, extendedPageSectionsTest/MenuTreeTest), PHPStan and CS-Fixer clean (local runner is a stub; CI runs PHPUnit)pages.htmlrules, 403s, DataTable list, menu validation (bad schemes, parents, depth, section with children), escaped sidebar, permission-gated items, reorder + delete, phone widthGenerated by Claude Code