` *after* the
- `in_admin_header` hook fires (confirmed directly in core's
- `admin-header.php`), but `ScreenHijack.php` started its output buffer
- *on* `in_admin_header` — so those wrapper divs' opening tags, and later
- their closing tags (printed before `admin_footer` fires), both ended up
- inside the buffer and got discarded along with the native content they
- were meant to replace. The mount div ended up a level too high in the
- DOM (a direct child of `#wpcontent`), which broke `windowChrome.js`'s
- `#wpbody-content`-based traffic-light insertion and the shell's card
- framing CSS. Fixed by having `ScreenHijack.php` re-print the `#wpbody`/
- `#wpbody-content` wrapper divs itself around the mount point, so the DOM
- matches what WordPress would have produced. Verified live on Posts,
- Media, Users, and Profile.
-
-## [2.13.0] - 2026-07-05
-
-### Added
-- Sixth and final phase of the "full admin takeover" initiative: the block
- editor (`post.php`/`post-new.php`). Deliberately a reskin, not a rebuild
- — Gutenberg stays completely native, extending the framing
- `windowChrome.js`/`shell-frame.css` already had working (traffic-light
- dots overlaid on the editor). Added a "← Posts"/"← Pages" breadcrumb next
- to the dots, resolved from the `post-type-{slug}` body class WP core
- already adds to every post edit screen (needed since Posts/Pages moved
- off a fixed `admin.php?page=...` URL onto their native URLs earlier this
- phase, so there's no single URL to hardcode).
- - Caught and fixed a real bug while testing: the Site Editor
- (`site-editor.php`) shares the same `block-editor-page` body class the
- breadcrumb logic keyed off, but has no `post-type-*` class (it edits
- templates, not a post) — the original version defaulted to guessing
- "Posts" there, which was wrong. Now returns no breadcrumb at all when
- no `post-type-*` class is present, verified live on both the post
- editor (crumb shows, correct link) and the Site Editor (no crumb,
- nested-skeleton framing unaffected).
- - This is the last screen in the "full admin takeover" initiative's
- original 9-item build order. All 9 phases are now shipped.
-
-## [2.12.0] - 2026-07-05
-
-### Added
-- Fifth phase of the "full admin takeover" initiative: Profile
- (`profile.php`), reskinning WordPress's own fields (name, contact info,
- bio) via core's `/wp/v2/users/me` REST route, plus a small new
- `ProfileData.php` route for password changes (core deliberately excludes
- password from its own REST schema). Free, gated by `modern_users`.
- - Deliberately does not attempt to host third-party plugin fields (2FA
- setup, WooCommerce customer fields, etc., added via the
- `show_user_profile`/`personal_options` hooks) — a Vue reskin has no
- safe way to render another plugin's raw HTML/JS. Instead, the hijack
- checks `has_action('show_user_profile')`/`has_action('personal_options')`
- at hijack time and falls through to the real native screen whenever
- either has a callback registered, rather than silently hiding those
- plugins' fields. Verified live both ways: installed a test mu-plugin
- hooking `show_user_profile` and confirmed the native screen renders
- instead of the reskin; removed it and confirmed the reskin returns.
- - Password changes re-issue the auth cookie immediately after
- `wp_set_password()` (which invalidates the current session, since it's
- derived in part from the password hash) — same as core's own
- user-edit.php does when you change your own password.
-
-## [2.11.0] - 2026-07-05
-
-### Added
-- Fourth phase of the "full admin takeover" initiative: the Appearance
- batch. Nav Menus (`nav-menus.php`) and Widgets (`widgets.php`) are real,
- purpose-built replacements — not wired into the existing Menu Creator,
- which manages Aurora's own admin sidebar and turned out to be an unrelated
- feature despite the similar name. Themes (`themes.php`) is a real
- list/activate/delete replacement (`ThemesData.php` + `Themes.vue`). Theme
- Editor (`theme-editor.php`) redirects to the existing File Manager instead
- of getting a second in-browser file editor — building one would duplicate
- File Manager's exact RCE risk surface for no benefit. Tools, Import/Export,
- and Site Health are deliberately left un-hijacked; Shell's existing
- native-page framing already covers them and a full rebuild has no real
- value-add over core's own screens.
- - Widgets is deliberately simplified to reordering/moving/deactivating
- existing widget instances between sidebars — adding a brand-new
- instance of an arbitrary widget type still uses WordPress's own
- block-based widgets screen (each widget type owns its own default
- settings and form; genericizing that is a full editor's worth of scope
- on its own, same reasoning as not rebuilding the block editor).
- - Nav Menus supports the common paths: create/delete menus, add
- pages/posts/categories/custom links, reorder and indent/outdent
- (sub-menu) items, assign menus to theme locations.
- - All three (plus Native Settings, previously shipped) are gated by the
- `full_admin_takeover` master toggle and the `"admin-takeover"` paid
- feature; every new REST controller checks the license directly in its
- own `permission_callback`, not just at the page level.
-- Fixed a real frontend bug caught while testing these against a 403 (no
- license): `Themes.vue`, `Widgets.vue`, and `NavMenus.vue`'s fetch calls
- didn't check `res.ok` before treating the response body as the expected
- data shape — a REST error object (`{code, message, data}`) got rendered
- as if it were the actual list, producing bogus placeholder rows instead
- of a clear error message. All three now surface the real error.
-- Verified live: Themes' activate/list/screenshot rendering confirmed
- working end-to-end (switched between all 3 installed themes); Nav Menus
- and Widgets' REST layer verified directly (create/add-item/list/delete
- round trip, real sidebar data) — the pages themselves couldn't be
- rendered in this dev environment since none of the 3 installed themes
- register classic nav-menu/widget support (all are block/FSE themes,
- which core itself refuses these screens for too — not a defect in the
- hijack).
-
-## [2.10.0] - 2026-07-05
-
-### Added
-- Third phase of the "full admin takeover" initiative: Media Upload
- (`media-new.php`) and Add New User (`user-new.php`) are now hijacked in
- place. Both are Vue-only — no new PHP REST controllers — talking directly
- to core's own `/wp/v2/media` and `/wp/v2/users` REST routes, which already
- cover file upload and user creation. Free, gated by the same
- `modern_media`/`modern_users` toggles as their already-free list-view
- companions (no reason to paywall mid-workflow between "browse" and "add").
- Verified live end-to-end: created a real user and confirmed it appears in
- the Users list.
-
-## [2.9.0] - 2026-07-05
-
-### Added
-- Second phase of the "full admin takeover" initiative: the 5 native
- Settings screens (General, Writing, Reading, Discussion, Permalinks) are
- now hijacked in place (`NativeSettingsPages.php`), sharing one generic
- Vue page (`NativeSettings.vue`) driven by a per-screen field schema.
- Most fields read/write through core's own `/wp/v2/settings` REST route;
- Permalinks has no core REST route for rewrite rules, so it gets its own
- small `PermalinksData.php` controller instead (calls
- `$wp_rewrite->set_permalink_structure()` + `flush_rules()`, same as core's
- own settings page does).
-- New master toggle, Settings > Admin Takeover > "Full admin takeover"
- (default **off** — these new areas cover more ground and carry more risk
- than the already-migrated Posts/Pages/Media/Users/Comments, so opt-in
- rather than on by default), plus a new `"admin-takeover"` paid feature key.
- Every new REST controller in this phase checks
- `License::is_feature_available("admin-takeover")` directly in its own
- `permission_callback`, not just at the page level — the same defense the
- File Manager REST controller already has, verified live by confirming a
- direct REST call still 403s with the setting on but no license.
-- `SettingsField.vue` (shared by the main Settings page and the new native
- Settings screens) gained two small additions: `readonly` support on text
- fields (Site Address shouldn't be editable inline) and a `select` field
- type (a plain dropdown, distinct from the existing multiselect checkbox
- list) — both minimal, generically useful additions, not one-offs.
-
-## [2.8.0] - 2026-07-05
-
-### Added
-- First phase of the "full admin takeover" initiative: Posts, Pages, Media
- Library, Users, and Comments no longer redirect to a separate
- `admin.php?page=...` URL — they now hijack their native screens in place
- (`ScreenHijack.php`, registered via `HijackedNativePages.php`), the same
- approach the original uixpress reference plugin used. WordPress's
- `load-{page}.php` hook fires before the native screen prints anything, so
- Aurora buffers and discards the native output and mounts its own Vue page
- in its place — the URL never changes, so there's no separate slug that can
- fall out of sync with a plugin's own redirect, a bookmark, or any other
- direct visit. This is exactly the bug class already hit and fixed once for
- Plugins; the other five pages are now structurally immune to it. Plugins
- itself stays on the redirect mechanism — `plugins.php` prints output in a
- way that can't be cleanly buffered, confirmed by the reference plugin's own
- abandoned attempt at hijacking it.
-- Existing REST controllers and Vue pages for all five are unchanged — only
- the mounting mechanism moved.
-
-## [2.7.2] - 2026-07-05
-
-### Fixed
-- Installing/activating a plugin dropped back to the native WordPress
- Plugins page instead of staying in Aurora's UI, until the sidebar's
- "Plugins" link was clicked again. Unlike Posts/Pages/Media/Users/
- Comments, Plugins only ever had its sidebar *link* rewritten
- (`MenuSerializer.php`) — there was no `redirect_native_pages()` case
- for direct visits to `plugins.php`, which is exactly what WordPress's
- own post-activate/post-install redirect does
- (`wp_redirect(self_admin_url('plugins.php?activate=true'))`, no
- `?page=` param). Added the missing case, matching the pattern already
- used for the other four pages.
-
-## [2.7.1] - 2026-07-05
-
-### Fixed
-- Native form fields (text inputs, textareas, selects) on several
- classic/non-block-editor screens — most visibly the title field on the
- Edit Media screen, but the same fix applies wherever it occurred — were
- showing stark white, breaking the dark theme. Root cause: some native
- WP screens style specific fields via a 2-ID selector (e.g. core's
- `#titlediv #title { background: #fff }`), which beats our existing
- rule's single `#wpbody-content` id no matter how many classes are
- stacked on it — the exact same CSS-specificity trap as an earlier
- block-editor background bug. Added `!important` to the form-field rule
- block, since there's no legitimate case where a native admin form field
- should stay white.
-
-## [2.7.0] - 2026-07-05
-
-### Added
-- New File Manager (`aurora-admin-file-manager`), browsing/editing the
- WordPress install's own files — built fresh (not embedding elFinder),
- in the spirit of the "WP File Manager" plugin already on this site
- (same root, same `manage_options`-only gate). Create/upload/download/
- rename/delete files and folders, plus a plain-text editor for
- code/config files. A **Pro feature** — the riskiest surface in the
- whole plugin (arbitrary read/write access to the site's own PHP files),
- so it's gated the same way as Database Explorer.
- - Every path a client sends is resolved to a real, `ABSPATH`-contained
- path via `realpath()` before any filesystem operation — containment
- is checked against the resolved path, not string-matched against the
- raw input, so symlink tricks and encoded traversal can't slip past.
- - Uploads/new files/renames all reject a blocklist of server-executable
- extensions (`.php`, `.phtml`, `.cgi`, `.sh`, etc.) — the single most
- important control here, since uploading a webshell is the whole ballgame.
- - Deliberately not built: zip/unzip (zip-slip extraction vulnerability),
- chmod/permissions editing, and cross-directory move/copy — each is a
- meaningfully larger, riskier scope than a first pass warrants.
-
-### Fixed
-- File Manager's REST endpoints now also check
- `License::is_feature_available('file-manager')`, not just
- `manage_options` — found while wiring it up that the page-level lock
- alone doesn't stop a direct REST call from bypassing it entirely.
- **The same gap exists in Database Explorer, Role Editor, and Activity
- Log's REST controllers and is not yet fixed** — flagged as a follow-up.
-
-## [2.6.0] - 2026-07-05
-
-### Added
-- Real replacement pages for Pages, Media Library, and Users — the
- remaining native admin screens that were only dark-themed skinning,
- now built the same way as Posts/Comments/Plugins (own REST controller,
- own Vue page, native screen redirected away):
- - **Pages** (`PagesData.php` + `Pages.vue`): same as Posts, but shows
- the parent page instead of categories. `edit.php?post_type=page`
- redirects; other post types are untouched.
- - **Media Library** (`MediaData.php` + `Media.vue`): a thumbnail grid
- (not a rebuilt drag-drop uploader) with type filters (Images/Audio/
- Video/Documents/Unattached), search, and delete. `upload.php`
- (both grid and list mode) redirects; `media-new.php` (the actual
- upload screen) is untouched, same reasoning as leaving the block
- editor alone for Posts/Pages.
- - **Users** (`UsersData.php` + `Users.vue`): filterable by role, with
- edit/delete actions. Deleting a user always reassigns their content
- to whoever performs the deletion, rather than exposing WP core's
- reassign-or-delete-everything choice as a one-click REST action —
- deliberately simplified to avoid a surprising, highly destructive
- default. `users.php` redirects; `user-new.php` (account creation)
- is untouched.
- - All four gated by their own Settings toggle (`modern_pages`,
- `modern_media`, `modern_users` — `modern_posts` already existed),
- each defaulting to on.
-
-## [2.5.0] - 2026-07-05
-
-### Added
-- Real replacement Posts management page (`aurora-admin-posts`), the same
- kind of full custom page as Comments/Plugins rather than dark-themed
- skinning over the native `edit.php` list table. New `PostsData.php` REST
- controller (list with status/search filters, trash, restore, permanent
- delete) and a new Vue page matching the Comments layout (status filter
- pills, search, table with title/author/categories/comments/date/status,
- inline row actions, pagination). The native Posts screen (`edit.php`,
- default post type only — Pages and other post types are untouched)
- redirects to it, gated by Settings > Posts' restored "Modern posts
- table" toggle (removed a few commits ago when it had no implementation
- behind it — now it does).
-
-### Fixed
-- The `modern_plugins`/`modern_comments` settings toggles now default to
- visually "on" in the Settings UI to match their actual PHP default
- (previously they defaulted true server-side but showed off in the UI
- until explicitly saved) — same fix applied to the new `modern_posts`.
-
-## [2.4.2] - 2026-07-04
-
-### Fixed
-- Admin Notices only ever showed on the Aurora Dashboard — everywhere
- else, `admin_notices` fired normally, but on the Dashboard specifically
- the notice was invisible: `DashboardTakeover` hides `#wpbody` entirely
- there (to hide the *native* dashboard, not our own), and that's exactly
- where WordPress prints `admin_notices` output. Notices now render
- directly inside the Aurora dashboard frame instead on that one page.
- Along the way, also worked around a separate WordPress core behavior
- (`common.js` unconditionally relocates every `.notice` element to right
- after the page's `
`) using core's own documented `below-h2` opt-out
- class, since without it the notice would've just been silently moved
- back into the hidden `#wpbody` by that script regardless of where it
- was placed server-side.
-
-## [2.4.1] - 2026-07-04
-
-### Fixed
-- Dashboard scrolling was broken — content below the first ~1500px
- (Server Health, Media Analytics, etc.) was completely unreachable.
- `.aurora-dashboard-frame` never got the height cap + internal scroll
- treatment `#wpbody-content` got in an earlier fix; combined with
- `html`/`body` being locked to `overflow: hidden` (to fix a phantom
- scrollbar on a different page), there was no scroll mechanism left at
- all for the dashboard specifically. Mirrored the same
- max-height/overflow-y treatment onto `.aurora-dashboard-frame`.
-
-## [2.4.0] - 2026-07-04
-
-### Added
-- Settings > Menu's "Menu search" toggle now actually does something —
- previously had no implementation at all. Shows a real search box at the
- top of the sidebar that filters both the WP-native menu list and the
- Aurora nav section by title, auto-expanding every section with a match.
-
-### Fixed
-- Settings > Dashboard's "Custom dashboard" toggle now actually gates the
- dashboard takeover — previously the Aurora dashboard always replaced the
- native one regardless of this setting, with no way to turn it back off.
-- Settings > Plugins' "Modern plugins page" and Settings > Comments'
- "Modern comments page" toggles now actually gate their respective
- takeovers/redirects the same way — previously both ran unconditionally
- regardless of these settings.
-
-### Removed
-- Settings > Posts' "Modern posts table" and Settings > Users' "Modern
- users page" toggles — both described a custom management interface that
- was never built. Shipping a checkbox with nothing behind it is worse
- than not having the checkbox; removed rather than left as dead UI. Their
- categories are removed too, since they had no other fields.
-
-## [2.3.1] - 2026-07-04
-
-### Fixed
-- Sidebar accordion now treats the "Aurora" branded nav section as part of
- the same single-open accordion as the WP-native menu list, instead of
- being the one exception that stayed open regardless of what else was
- expanded. Expanding any native section now collapses Aurora too, and
- vice versa.
-
-## [2.3.0] - 2026-07-04
-
-### Added
-- Settings > Media's "Allow SVG uploads" toggle now actually does
- something — previously existed in the UI with no implementation behind
- it, so every SVG upload was rejected. Fixes both of WordPress's default
- blocks (the `upload_mimes` allowlist and the real-mime sniff that nulls
- out the file type for SVGs specifically), plus a PHP warning
- (`wp_get_missing_image_subsizes()` reading undefined `width`/`height`
- metadata keys that SVGs never had) that could otherwise corrupt the
- upload response on environments with warnings displayed inline. Also
- does basic sanitization on upload (strips `