Batches everything built since the 1.0 WP.org submission reset: Login Redirect, the Tools/Export/Import screen takeovers, Modules rebuilt as an addon marketplace, Menu Creator's reorder fix, Activity Log redesigned into a filterable table with Admin/User tabs, dark-theme fixes for Site Health/Personal Data/Updates, and an opt-in Disable Gutenberg toggle. Continues the "nothing stays native" takeover goal and closes gaps found via live testing and the WordPress Plugin Check tool. See CHANGELOG.md for the full per-version breakdown (1.1-1.12). Co-Authored-By: Claude Sonnet 5 <[email protected]>
75 KiB
75 KiB
Changelog
All notable changes to Aurora Admin are documented here, one entry per
version bump. Starts at 2.1.0 — earlier history (the theming rebuild,
window chrome/taskbar, Site Editor support, etc.) lives in git log and
PROJECT_STATUS.md, not reconstructed here.
[3.6.1] - 2026-07-11
Fixed
- Second round of misplaced
phpcs:ignorefixes, found via another fresh Plugin Check report after 3.5.1's fix. Turns outWordPress.DB.DirectDatabaseQuery.DirectQuery/NoCachingandWordPress.DB.PreparedSQL.NotPrepared/InterpolatedNotPreparedare attributed by PHPCS to different lines within the same multi-line$wpdb->get_results()/get_row()call —DirectQuery/NoCachingto the outer call's opening line,NotPreparedto whichever inner line the interpolated table name physically appears on. 3.5.1's fix moved every ignore comment down to cover the inner line (fixingNotPrepared) but broke coverage of the outer line (DirectQuery/NoCachingcame back) for four calls inMenuCreatorData.phpandNotices.php/NoticesData.phpthat hadn't already been collapsed to a single line. Fixed by collapsing those four remaining multi-line calls too — consistent with the single-line-call pattern used everywhere else in the 3.5.1 fix, which a single ignore comment reliably covers regardless of which sniff PHPCS attributes to that line. Re-verified with the automated scan plus live REST-dispatch functional round-trips.
[3.6.0] - 2026-07-11
Changed
- Site Backup extracted into a standalone companion plugin, at the
user's explicit request, following the same pattern as File Manager
(3.4.0) and Database Explorer (3.5.0) — proactive risk reduction, not a
WP.org rejection: restoring a backup means writing arbitrary files back
into the site, the same class of thing that's kept file-management
functionality out of Aurora Admin proper.
- The entire
admin/src/Backup/tree (job/schedule storage, DB dumper, file archiver, the state-machine engine, wp_cron safety net, and all four storage providers — local, S3-compatible, Google Drive, plus the provider interface/factory) and all four Backup REST controllers moved, unmodified in behavior, into a brand-new sibling plugin: Aurora Site Backup (aurora-site-backup.php, own bootstrap, own REST namespaceaurora-site-backup/v1, own Vite build). Since this plugin needs its own settings storage independent of Aurora Admin (S3 credentials, Google Drive OAuth tokens), a small standaloneOptions\Settingsclass was added, backed by its ownaurora_site_backup_settingsoption — same minimal shape as Aurora Admin's own Settings class, just a separate store. CompanionPluginData.php'sPLUGINSconfig array (already generalized in 3.5.0) gained asite-backupentry — no new PHP needed beyond the config line. The Settings → Add-ons screen now lists all three companion plugins.- Verified live via a full
rest_do_request()dispatch: created a real backup job, stepped it through the DB-dump phase, and canceled it — end-to-end confirmation the extracted code still works, not just that it loads without fatals. - Deployment gap, same as the other two companion plugins: the
aurora-site-backup.zipis not yet hosted athttps://aurora.auroradragon.studio/aurora-site-backup.zip— the Add-ons "Install & Activate" button for it will fail with a fetch error until that's deployed (needs explicit go-ahead).
- The entire
[3.5.1] - 2026-07-11
Fixed
- Misplaced
phpcs:ignorecomments from the 3.3.2 compliance pass, found via a fresh Plugin Check report. WPCS'sphpcs:ignoresuppresses violations reported on the exact next physical line — but several annotations added in 3.3.2 sat above the opening line of a multi-line$wpdb->get_results()/get_row()/get_var()call (e.g.$rows = $wpdb->get_results(), while PHPCS actually attributes the violation to whichever line further down inside that call contains the interpolated table-name/column tokens. Those suppressions were silently never taking effect. Fixed acrossMenuCreatorData.php,NoticesData.php,Notices.php,BackupSchedules.php,BackupJobs.php,MediaFolders.php(the largest cluster — collapsed most multi-line query calls to a single physical line so one ignore comment reliably covers every sniff PHPCS reports on that line, added aMediaFolders::name_taken()helper to replace an inlinewhile()condition that couldn't otherwise be annotated),DbDumper.php,AnalyticsData.php, andMediaData.php'spost__not_inVIP-advisory comment. Also added a genuinely missing annotation onAnalytics::record()'s$wpdb->insert()call. Verified with an automated scan confirming every$wpdb->call with dynamic table/query construction is now immediately preceded by its ignore comment. - Real bug found during verification, unrelated to Plugin Check:
MenuCreatorData::create()built its internal follow-up "fetch the row I just inserted" request asnew \WP_REST_Request("GET", "", ["id" => $wpdb->insert_id])— butWP_REST_Request's third constructor argument doesn't populate whatget_param()reads, soget_one()always received a null id and returned "Menu not found" instead of the newly created menu. Confirmed via a fullrest_do_request()dispatch (not just a direct function call) that this reproduced through the real REST route. Fixed by building the request withset_url_params(), WP core's own documented pattern for this. - Confirmed the
compressed_files/hidden_files/unexpected_markdown_filefindings in the Plugin Check report (build zips,.gitignore,.claude/,PROJECT_STATUS.md) are scanner noise from scanning the whole development repository, not the actual submission zip — verified viaunzip -lthat none of those paths exist inwporg-submission/aurora-admin.zip.
[3.5.0] - 2026-07-11
Changed
- Database Explorer extracted into a standalone companion plugin, for
the same reason and following the exact same pattern as File Manager
in 3.4.0: direct database-query tooling (browsing tables, running
free-form SELECT/SHOW/DESCRIBE/EXPLAIN queries from wp-admin) is
treated by WordPress.org's Plugin Directory review the same way as
file-management functionality — outside what it accepts in a
directory-listed plugin. Rather than wait for a rejection to confirm
it or delete the feature outright:
admin/src/DatabaseExplorer/DatabaseExplorerData.phpand its Vue UI (plus the sharedAppListShell/AppListItemlist-detail layout it's built on) moved, unmodified in behavior, into a brand-new sibling plugin: Aurora Database Explorer (aurora-database-explorer.php, own bootstrap, own REST namespaceaurora-database-explorer/v1, own Vite build). Works completely standalone, verified live via direct HTTP request with a server-generated auth cookie — the admin page renders its mount div and enqueued script correctly, and the REST namespace is registered and reachable.CompanionPluginData.phpwas generalized from a single file-manager-only class into a smallPLUGINSconfig array keyed by slug, so both File Manager and Database Explorer (and any future companion plugin) share the same install/activate/status REST routes andPlugin_Upgrader-based installer logic — adding a new companion plugin going forward is a config entry, not new PHP.- The Settings → Add-ons screen now lists both companion plugins.
- Deployment gap, same as File Manager: the Database Explorer zip
is not yet hosted at
https://aurora.auroradragon.studio/aurora-database-explorer.zip— the Add-ons "Install & Activate" button for it will fail with a fetch error until that's deployed (needs explicit go-ahead, same as any other production deploy).
[3.4.0] - 2026-07-11
Changed
- File Manager extracted into a standalone companion plugin. WordPress.org's
second review response was unambiguous: the entire category of
file-management/code-editing functionality isn't accepted into the
Plugin Directory, regardless of the restrictions added in v3.2.1 —
the feature had to come out of Aurora Admin entirely for the
submission to be reconsidered. Rather than delete the functionality:
admin/src/FileManager/FileManagerData.phpand its three Vue components moved, unmodified in behavior, into a brand-new sibling plugin: Aurora File Manager (aurora-file-manager.php, own bootstrap, own REST namespaceaurora-file-manager/v1, own Vite build). It works completely standalone — verified live with Aurora Admin fully deactivated.- The Theme Editor → File Manager redirect (
AppearancePages.php) is gone; visits totheme-editor.phpnow fall through to WordPress core's own Theme Editor unmodified, which already respectsDISALLOW_FILE_EDITitself. - A new Settings → Add-ons screen offers one-click installation of
Aurora File Manager, wrapping
Plugin_Upgrader::install()— the same core API the native Plugins → Add New "Install Now" button uses — pointed at a fixed, non-client-supplied HTTPS zip URL. No settings/data are shared between the two plugins; Aurora Admin has zero remaining code that touches the filesystem beyond its own Site Backup archives.
[3.3.2] - 2026-07-11
Fixed
- WordPress.org Plugin Check compliance pass — a full re-run of the
official Plugin Check tool against the whole codebase (279 findings
across 26 files) surfaced several real gaps beyond the earlier v3.0.1/
v3.0.2/v3.2.1 passes, all now fixed:
readme.txt'sStable taghad drifted to 3.2.1 while the plugin itself moved through 3.3.0/3.3.1 — a real mismatch that can prevent WordPress.org from serving the correct download to users. Now kept in lockstep with every version bump.Analytics::country()read$_SERVER[$h]directly for anempty()/strlen()check before the existing sanitized copy was computed — fixed by sanitizing once up front and using that everywhere, rather than touching the raw superglobal at all.AuroraAdmin.php'sdefined("ABSPATH") || exit()guard sat after 49useimport statements — functionally fine, but far enough into the file that at least one automated file-access-protection scanner didn't detect it. Moved to immediately after thenamespacedeclaration, this file's one deliberate departure from the rest of the codebase's "guard after imports" convention.- Roughly 230
WordPress.DB.DirectDatabaseQuery/PreparedSQL/UnescapedDBParameterfindings across every custom-table feature (Site Backup, Media Folders, Activity Log, Analytics, Menu Creator, Notices, Database Explorer) — verified each one is safe (internal$wpdb->prefix-based table names or already-validated/escaped identifiers, never raw user input) and annotated with the justifiedphpcs:ignorethe review process expects, matching the standard already established for Database Explorer's query runner. - A handful of
fread()/fwrite()/fclose()calls adjacent to an already-annotatedfopen()were still individually flagged — WPCS suppression is strictly per-line, so each sibling call in Site Backup's file/S3/Google Drive streaming code now has its own annotation too. - Several
RuntimeExceptionmessages interpolating internal (never user-supplied) values were flagged as unescaped output — annotated as exception messages, not HTML/browser output, the same false-positive class WPCS is known to flag on exception constructors.
[3.3.1] - 2026-07-11
Fixed
- Settings page: nav and content now scroll independently. Previously the left category list and the right content panel scrolled together as one long page — scrolling down the nav to reach a category near the bottom (e.g. "Modern Image Formats") and clicking it left the content panel's top scrolled out of view above the fold, requiring an extra scroll back up to actually see it. Both panels now have their own fixed-height scroll region, and switching categories resets the content panel's scroll position to the top regardless of the nav's own scroll position.
[3.3.0] - 2026-07-11
Added
- Modern Image Formats — a new Settings section that automatically
generates a WebP or AVIF sibling for every size WordPress produces from
a newly uploaded JPEG/PNG, and (optionally) rewrites
<img>markup into a<picture>element offering the modern format with the original as a fallback source. "Automatic" output format prefers AVIF, falling back to WebP, based on what the server's image editor actually supports (wp_image_editor_supports()) — gracefully generates nothing on a host that supports neither, rather than erroring. Purely additive: original JPEG/PNG files are always kept, nothing about the attachment's own metadata or file is changed, and generated files are cleaned up automatically when the attachment is deleted. Only affects new uploads — existing library images need a regenerate-thumbnails-style tool to pick up the feature.- Built entirely on documented WordPress core hooks
(
wp_generate_attachment_metadata,delete_attachment,wp_editor_set_quality,wp_content_img_tag,wp_get_attachment_image_html) andWP_Image_Editor::save()'s documented mime-type-conversion parameter — no third-party plugin code, no Composer dependency. - Verified live end-to-end: uploaded a real JPEG, confirmed a smaller
AVIF sibling was generated and recorded in attachment metadata,
confirmed a post embedding the image rendered a correct
<picture><source type="image/avif" srcset="...">wrapper, and confirmed both the original and generated files were removed with no orphans left behind after deleting the attachment.
- Built entirely on documented WordPress core hooks
(
[3.2.1] - 2026-07-11
Changed
- File Manager: PHP/executable editing removed. Rejected from the
WordPress.org Plugin Directory because File Manager let a
manage_optionsuser open and overwrite existing.phpfiles in-place with immediate execution effect — no different in risk from WordPress core's own Theme/Plugin editor, which core lets hosts disable viaDISALLOW_FILE_EDITspecifically because of that risk. File Manager previously blocked creating/uploading/renaming-to executable file types, but that same list wasn't enforced on editing an existing file of that type — closed now: opening or saving any file matchingBLOCKED_EXTENSIONS(php, phtml, cgi, py, sh, and others) is refused. File Manager also now honorsDISALLOW_FILE_EDIT(blocks all editing) andDISALLOW_FILE_MODS(blocks all create/upload/rename/delete/copy/ move/write operations), the same host-level lockdown constants WordPress core's own file-modification flows already respect. Browsing, downloading, and viewing file info are unaffected.
[3.2.0] - 2026-07-11
Added
- Google Drive remote storage for Site Backup — a second remote
storage option alongside S3-compatible, via OAuth 2.0. Each site
connects its own Google Cloud OAuth client (Client ID + Secret, pasted
into Settings) since a free, publicly-distributed plugin can't ship a
shared client secret; scope is the narrow
drive.file(only files/folders Aurora itself creates). Resumable upload andRange-header chunked download against Drive API v3 mirror the bounded-per-step design S3's multipart upload already established — no changes to the job engine itself were needed, confirming the storage-provider abstraction's original design goal. Shipped code-complete and protocol-verified (request shapes checked against Google's published API reference, and live-tested against Google's real OAuth endpoints with placeholder credentials to confirm genuine API errors come back); a full live "Connect" round-trip is left for the user to verify once they have a real Google Cloud OAuth client and HTTPS on the target site.
[3.1.0] - 2026-07-11
Added
- Site Backup — a new free sub-app: full site backup (database +
wp-content) and restore-in-place, on-demand or on a recurring daily/
weekly/monthly schedule, storing archives locally or on any
S3-compatible remote (AWS S3, Backblaze B2, Cloudflare R2, DigitalOcean
Spaces — one implementation via a configurable endpoint, no vendor
SDK, SigV4 signing implemented directly and verified against AWS's own
published test vectors).
- Built on a new resumable job-processing engine (
BackupEngine+BackupJobs) — the first background-job infrastructure in this codebase. Every backup/restore step is bounded (row/file/byte count and a wall-clock budget) so a multi-GB site never risks a single request's timeout or memory limit. Two trigger paths converge on the same step function: foreground polling from the Vue UI (fast feedback while a tab is open) and awp_cronsafety net (BackupCron) that keeps a job moving with no browser involved — closing the tab mid-backup, or a scheduled backup with nobody watching, both still reach completion. - Database export/import (
DbDumper) — paginated dump with schema captured per-table (DROP TABLE IF EXISTS+CREATE TABLE, so restore is deterministic and re-runnable from scratch), chunked restore via a byte-cursor into the dump file. Aurora's own backup-tracking tables are excluded from what gets backed up — a real bug caught during testing, where restoring a snapshot of those tables was overwriting the very job row driving the restore mid-flight. - File archiving (
FileArchiver) — a manifest of every file underwp-content/(excluding the backup storage directory itself and, structurally,wp-config.php, which lives outsidewp-content/entirely), zipped/extracted in bounded batches viaZipArchive, reopened and appended to across isolated requests. Every extracted entry is containment-checked (zip-slip protection) before anything is written — verified live with a deliberately crafted malicious archive entry, confirmed rejected. - Local storage hardening —
wp-content/uploads/aurora-backups/gets WordPress core's ownindex.php/.htaccessstub pair; the real protection is that every archive read goes through amanage_options-gated REST route streaming the file through PHP, never a guessable direct URL. - Recurring schedules with count-based retention — a scheduled backup prunes older ones (both the job row and its stored archive, local or remote) down to the schedule's configured retention count.
- v1 deliberately does not include cross-domain migration (Duplicator's
own headline feature — generating an
installer.phpfor a totally different host) or restoring from an arbitrary uploaded archive (restore only ever sources from Aurora's own backup history) — both are real scope/risk decisions, not oversights.
- Built on a new resumable job-processing engine (
- Live-verified end-to-end on the dev site, including catching and fixing
two real bugs along the way: a job's
phasecolumn wasn't updated to a clean terminal value on completion (stale "Archiving files…" label after a backup had actually finished), and a data-corruption bug where manually pre-escaping newlines before$wpdb->_real_escape()(which already does this) caused restored text fields to contain literal backslash-n characters instead of real line breaks.
[3.0.2] - 2026-07-11
Fixed
- Plugin header validation error from the WordPress.org submission checker:
Plugin URIandAuthor URIwere bothhttps://auroradragon.studio, and WP.org requires them to be distinct (one identifies the plugin, the other the author).Plugin URInow points tohttps://aurora.auroradragon.studio(the plugin's own marketing site);Author URIstays on the studio's root site.
[3.0.1] - 2026-07-11
Fixed
- WordPress.org Plugin Check compliance pass. Ran the official Plugin
Check tool ahead of submission and fixed every real issue it surfaced
(257 flagged items, several dozen real):
WidgetsData.php: replaced the forbiddenwp_get_sidebars_widgets()(internal-only core function) with a local helper reading thesidebars_widgetsoption directly.FileManagerData.php: converted every raw filesystem call (move_uploaded_file(),mkdir(),rename(),unlink(),rmdir(),is_writable(),readfile()) to theWP_FilesystemAPI, which works across hosting setups that don't allow direct filesystem access.move_uploaded_file()'s upload-tampering check is preserved via an explicitis_uploaded_file()call before handing off toWP_Filesystem::move().BugReportData.php:parse_url()→wp_parse_url().SubApps.php: fixed a translation-function rule violation (page titles now wrapped in__()at definition instead of being passed as a variable to__()at the call site) and added justified nonce-check suppressions on two read-only routing checks.- Fixed missing
wp_unslash()/sanitization on a handful of superglobal reads (Analytics.php,Shell.php,AnalyticsData.php) and added missing translator comments for two placeholder strings. - Annotated dynamic-table-name SQL queries across
MediaFolders.php,AnalyticsData.php,DatabaseExplorerData.php,NoticesData.php,MenuCreatorData.php, andActivityLogData.phpwith justifiedphpcs:ignorecomments — every flagged table name comes from an internal$wpdb->prefix-based helper, never user input, and table identifiers can't go through$wpdb->prepare()'s value placeholders (WordPress core has no API for arbitrary custom tables). - Removed the stale
Domain Path: /languages/plugin header (pointed at a folder that's never existed — see 2.x-era removal of the fake "13 languages" marketing claim). - All changes live-verified against the LocalWP install: File Manager's full create/rename/delete cycle exercised directly against the REST routes (folder create, file create, rename, soft-delete, empty trash), Widgets' sidebar-read endpoint, Database Explorer's table browsing, and Media Folders' filtered listing all confirmed working with zero console/PHP errors.
[3.0.0] - 2026-07-06
Removed
- The entire licensing/monetization system. Aurora Admin is being given
away free on the WordPress.org plugin repository — every feature
previously gated behind a paid license (Role Editor, Activity Log,
Database Explorer, File Manager, Analytics, White Label, Full Admin
Takeover, Media Folders) is now simply available to everyone, no key
required.
- Deleted
admin/src/Licensing/entirely (License.php,Integrity.php, the file-tamper-detection system that only existed to protect license gating from bypass) andbin/build-integrity-manifest.php. - Deleted the "Pro feature" upgrade-prompt UI (
UpgradeNotice.vue,locked-feature.js) and every lock badge in the sidebar. - Removed the License key field and the dev-only "Simulate active license" toggle from Settings — there's nothing left to license or simulate.
license_keyanddev_license_overrideare actively purged from any site's stored settings on upgrade (same cleanup pattern used for the earlier Remote Site Switcher removal), not just hidden from the UI.- Report a Bug still works, now via a direct call to the feedback endpoint instead of going through the license-status layer.
- This is a clean split from the commercial
auroraplugins.comservice (beta licenses, Pro purchases) — that server-side system is untouched and unaffected by this change, it's simply no longer wired into this codebase.
- Deleted
[2.27.0] - 2026-07-06
Added
- Restored the dev-only license override (
AURORA_ADMIN_DEV_MODEwp-config.php constant + Settings > Developer > "Simulate active license" toggle), reversing the 2.25.0 removal. Requested explicitly for local testing on admin.local — lets Pro features be exercised without a real license key. This must be stripped again before the next release/beta build — same standing rule as before, now re-documented inPROJECT_STATUS.md. Only has any effect whenAURORA_ADMIN_DEV_MODEis defined truthy in a given install's ownwp-config.php, never set in the repo or any built zip.
[2.26.0] - 2026-07-05
Added
- "Report a Bug" page (Aurora sidebar section), a real WordPress admin page
(
aurora-admin-bug-report, registered viaSubApps.phplike Menu Creator/Admin Notices) with a single free-text field plus auto-filled, read-only diagnostics (plugin version, WordPress version, PHP version, active theme, last 4 characters of the license key — not the full key). Submits to a newPOST aurora-admin/v1/bug-reportREST route (admin/src/Rest/BugReportData.php), which forwards to a newLicense::submit_bug_report()method that calls the license server.- License server (
aurora-licensingplugin, not this repo): added awp_aurora_bug_reportstable (DB version 3 → 4, same version-gated migration idiom) and a new publicPOST /report-bugREST route, storing the description plus whatever license key/domain/diagnostics fields were sent. Deliberately a plainwpdb->insertwith no admin UI yet — reports are reviewable viawp db queryfor now; a dashboard can come later if report volume justifies it. - Verified live end-to-end (not mocked): submitted a real report through
the actual Report a Bug page, confirmed it landed correctly in
wp_aurora_bug_reportson the production server, then deleted the two test rows (one from an earlier curl smoke test) by exact ID.
- License server (
[2.25.0] - 2026-07-05
Removed
- Dev-mode license override (
AURORA_ADMIN_DEV_MODEwp-config.php constant, Settings > Developer category's toggle, and every call-site inLicense::is_active()/is_feature_available()) — this was a build-time-only escape hatch for testing paid features without a live license, marked inPROJECT_STATUS.mdas "MUST BE REMOVED BEFORE A RELEASE BUILD" since it shipped in the plugin zip. Real beta testers are about to install real builds, so it had to go before that zip goes out. Paid features now gate purely onLicense::is_active()for everyone, no exceptions.
[2.24.0] - 2026-07-05
Added
- Beta tester license support, end to end. User asked for 10 new 30-day
beta licenses plus a way to flag/display beta status in the plugin.
- License server (
aurora-licensingplugin on the liveauroraplugins.combox, not this repo): added anis_betacolumn towp_aurora_licenses(migrated in place via aplugins_loaded-hooked check, since the file was updated without re-activation — same version-gated migration idiom this plugin's ownActivityLog.phpetc. use), awp aurora-lic generate-beta <count> [--days=<n>] [--max-sites=<n>]WP-CLI command (not a public REST route — generating free licenses has to stay admin/shell-only), andis_betaadded to/validateand/activate's response payloads. Generated the 10 requested keys (30-day expiry, 1 site each) — see the conversation for the actual key list; not duplicated here since this file ships to customers. - Client (this plugin):
License::is_beta_tester()reads the server'sis_betaflag (already flowing through the existingstatus()cache — no new caching logic needed). Settings > General's license status line now shows "Beta Tester license active — expires in N days (date)" instead of the normal "License active (plan)" text when a beta key is active. A beta license is a real, fully-functionalis_active()license like any other — this is a display flag only, not a separate gating path. - Verified against the real production license server, not a mock:
generated a real key via the new CLI command, activated it from the
dev site's actual Settings page, confirmed the badge and expiry
countdown render correctly, then cleared the key (deactivating it
server-side via the existing
on_settings_updatedflow).
- License server (
[2.23.0] - 2026-07-05
Added
- Full field-set parity for Writing/Reading/Discussion native Settings
screens, closing the gap flagged in 2.22.0's audit.
- Writing:
Default Post CategoryandDefault Post Formatare now real dropdowns (populated fromget_categories()/get_post_format_strings()) instead of raw numeric IDs. Addeduse_smilies— registered in core's own REST settings schema but never actually added to this screen. - Reading:
Homepage/Posts Pageare now page-title dropdowns (viaget_pages()) instead of raw page IDs, and only appear once "Homepage Displays" is set to "A static page" — matching native WordPress's own conditional reveal. New genericshowIffield property inNativeSettings.vue({key, equals}) drives this, reusable by any future field. - Discussion: went from 2 fields to full native parity (~24 fields:
pingback/comment defaults, moderation rules, threaded-comment
settings, pagination, three notification-email toggles, comment
moderation/disallowed-keys blocklists, and avatar settings). Core's
REST settings schema (
register_initial_settings()) only ever exposed 2 of Discussion's real options — everything else needed a new dedicated controller,DiscussionData.php, doing rawget_option()/update_option()reads/writes against an explicit key allowlist (same pattern asPermalinksData.php, generalized to many keys instead of one). - New
textareafield type inSettingsField.vuefor the two moderation-keys blocklist fields (multi-line plain text — nothing else on any settings screen needed one until now). - Verified live: dropdowns populate with real data, conditional fields show/hide correctly, and a real save-then-reload round trip on Discussion (toggled a setting off, saved, reloaded fresh, confirmed the change persisted) — not just an optimistic-UI check.
- Writing:
[2.22.0] - 2026-07-05
Full plugin audit, requested after the plugin-delete crash raised the question of what else might be broken or incomplete. Checked every sidebar screen live (console + PHP error log at each stop), re-verified the license-activation claim from last session against the actual code, and closed the one remaining documented security gap.
Fixed
- REST-bypass gap closed for Role Editor, Activity Log, and Database
Explorer — their REST controllers only checked
current_user_can('manage_options'), not the license, so a direct REST call could reach paid functionality (including Database Explorer's raw SQL runner) with no license at all. Same fix already applied to File Manager/Media Folders earlier this session, just never circled back for these three. Verified every other paid-feature REST controller (Themes, Nav Menus, Widgets, Permalinks, Analytics, White Label) already had the check — this was the last of them. - Permalinks screen was missing the standard structure presets (Plain, Day and name, Month and name, Numeric, Post name) — only the raw "Custom Structure" text field existed, which is the one part of that screen almost nobody actually uses by hand. Added the five presets as radio options with live example URLs, auto-detecting which preset (or "Custom") matches the currently-saved structure.
Verified (no changes needed)
- Clicked through Dashboard, Posts, Pages, Media, Users, Comments, Plugins, all Aurora sub-apps (Menu Creator, Admin Notices, Role Editor, Activity Log, Database Explorer, File Manager), Media Upload, Add New User, Profile, all 5 native Settings screens, and several Aurora Settings categories (General, White Label, Analytics) — no fatals, no new console errors anywhere in the pass.
- Confirmed License.php's activation/validation flow is real (calls
auroraplugins.com's REST API, caches with TTL, tamper-checks) — corrected a stale "still a placeholder" claim from earlier this session and removed the outdated doc line that caused it.
Known gaps (not fixed — flagged for a decision, not silently patched)
- Writing/Reading/Discussion native Settings screens expose only a handful of fields each, several as raw text/numeric-ID inputs where native WordPress has dropdowns (post category, post format, homepage page picker) — functional but poor UX, and Discussion in particular is missing most of its native moderation options. Larger effort than the Permalinks fix; needs new endpoints (categories list, post formats) and new field types, not just a values tweak.
- Comments list's pagination footer shows "0 to 0 of 0" despite the "All" tab correctly showing a count — cosmetic, not investigated further.
[2.21.2] - 2026-07-05
Fixed
- Deleting a plugin from Aurora's Plugins page ("major issue" per user
report) fataled the whole site with WordPress's generic "There has been
a critical error" page instead of actually deleting anything. Root
cause, found in the PHP error log:
PluginsData::delete_plugin()called core'sdelete_plugins(), which internally callsrequest_filesystem_credentials()(WP_Filesystem setup) — that function lives inwp-admin/includes/file.php, which a REST API request doesn't load the way a normal wp-admin page load does, anddelete_plugin()only requiredplugin.php.update_plugin()right below it already requiredfile.phpfor the same underlying reason;delete_plugin()just never got the same fix. Verified live: deleted a real plugin (WP File Manager) end to end with no fatal, "Plugin deleted." message shown correctly, confirmed no new error logged.
[2.21.1] - 2026-07-05
Fixed
- Sidebar section accordion closed the wrong section on "hidden child"
native screens (theme-install.php, media-new.php, user-new.php, ...).
User: expand Appearance, click "Add New Theme", and the Appearance
section collapsed while the "AURORA" section at the bottom opened
instead — this was a different bug than the overlay-covers-the-sidebar
issue fixed in 2.21.0, this was specifically about which section stays
expanded. Root cause:
Sidebar.vue's accordion decides the open section by matching the current URL against each menu item's own submenu array (MenuSerializer::serialize(), scraped from WP's$submenuglobal) — but these screens are deliberately absent from$submenu(WP's own native left menu doesn't list "Add New Theme" as a link either, only "Themes"/"Customize"/"Editor"), so no section ever matched and the accordion fell through to its Aurora-expanded default.- WP core itself solves this exact problem for its own native menu
highlighting via a
$parent_fileglobal, set directly in each of these pages' own PHP (theme-install.phpsets$parent_file = 'themes.php',media-new.phpsets'upload.php',user-new.phpsets'users.php') — reused that instead of hardcoding a list of "hidden child screen" mappings ourselves.Shell::print_mount()now reads it and passes it through asactiveParentFile;Sidebar.vue'sisSectionActive()treats a menu item as active when its id matches, alongside the existing exact-URL checks. General fix — covers any current or future WP core (or third-party plugin) screen using this same convention, not just the three confirmed here.
- WP core itself solves this exact problem for its own native menu
highlighting via a
[2.21.0] - 2026-07-05
Changed
- The theme-install.php "Preview & Install" overlay (clicking a theme card
under Appearance > Themes > Add New Theme) is now framed like the block
editor window instead of covering Aurora's sidebar/toolbar entirely.
User reported "it closes the menu" — confirmed this is WP core's own
native behavior (the fullscreen overlay covers the native admin menu
too, on a totally vanilla install), not a regression, but agreed a
framed treatment was worth the risk for visual consistency with how the
block editor is already handled.
.wp-full-overlay(WP core's overlay component, shared with the Customizer — only.theme-install-overlayspecifically is touched, not.wp-full-overlaygenerally) is reframed to Aurora's standard inset box: the overlay itself plus its two independentlyposition: fixedchildren (.wp-full-overlay-sidebar,.wp-full-overlay-main) each need the same left/top/bottom math applied directly, since fixed descendants don't inherit an ancestor's padding-box positioning the way absolutely-positioned ones do.margin-lefton.expanded/.collapsedmirrors WP core's own three responsive breakpoints with Aurora's frame offset added on top, so the sidebar collapse/expand slide animation keeps working unmodified.- Root-caused and fixed two real bugs found while building this: (1)
opening the overlay adds
body.full-overlay-active, and WP core's own CSS setsbody.full-overlay-active { visibility: hidden }with only the overlay itself set back to visible — Aurora's entire shell inherited that hidden visibility too (nested in body), which is what silently erased the sidebar/toolbar instead of just having them covered. Fixed with one override re-enabling visibility for Aurora's own root specifically. (2) The overlay's ownheight: 100%(WP core) combined with ourtop+bottominsets was the exact same over-constrained-box bug already known from the block editor's.interface-interface-skeleton— sameheight: auto !importantfix. - The traffic-light winbar needed a permanently-running
MutationObserverrather than a one-shot check: the overlay doesn't exist in the initial DOM (WP core creates it lazily on first click), and every subsequent theme selection fully re-renders the overlay's contents, wiping out anything inserted into it — confirmed live (bar present after the first click, gone after picking a second theme) before switching to an idempotent "ensure inserted" observer callback. - Verified live end-to-end: opening via natural clicks (not just direct URL navigation), switching between themes with the prev/next arrows (triggers the content-destroying re-render), closing and confirming the sidebar/toolbar return correctly, no console errors at any step.
[2.20.2] - 2026-07-05
Fixed
- Appearance > Themes was missing two capabilities the native Themes
screen has: browsing/installing themes from the WordPress.org directory,
and uploading a theme zip.
AppearancePages.php'sload-themes.phphijack never passed a link totheme-install.php(onlycustomizeUrl), so there was no way to reach either from Aurora's Themes page.Themes.vuegains an "Add New Theme" button (next to Customize) linking to nativetheme-install.php, gated oncurrent_user_can('install_themes')— that native screen already has both the WordPress.org browser (Popular/ Latest/Block Themes/Favorites/Feature Filter tabs, search) and an "Upload Theme" button built in, so neither needed rebuilding. Same reuse-native-flow-for-add pattern already used for Plugins'addUrl(plugin-install.php) and Media'snewMediaUrl(media-new.php).
[2.20.1] - 2026-07-05
Fixed
- Font Library (
font-library.php) rendered with a jarring mix of Aurora's dark frame around a completely white, near-unreadable content area (light text on a white card). WP 6.5+ built this screen on@wordpress/ components' newer "boot-layout" primitives instead of classic postbox markup, sonative-dark.css's existing postbox/card rules never matched its DOM. Added targeted dark-theme coverage for the screen's stable, hand-authored class names (.boot-layout-container,.boot-layout__stage,.admin-ui-page,.admin-ui-navigable-region,.admin-ui-page__header,.font-library-page__tab-panel,.font-library__tabpanel-layout,.font-library__footer,.font-library__fonts-list-item,.font-library__font-card) — deliberately not touching this screen's Emotion-generated hash classes (e.g.css-qi2azi), which aren't stable across WP builds. - Regenerated
admin/src/Licensing/integrity-manifest.jsonand rebuiltdist/aurora-admin-v2.zipfrom a fresh staging copy — the protected files' checksums (Integrity::is_tampered()'s manifest) had gone stale after this session's edits toaurora-admin.php,Shell.php, and others. Also excluded.claude/from the staged copy — the internal tooling config had been getting bundled into the distributable zip.
[2.20.0] - 2026-07-05
Added
- Media Folders (paid —
License::PAID_FEATURES["media-folders"]): a folder tree for the Media Library, duplicating the organizing capability of the FileBird Pro plugin at the user's request, with its own independent schema (wp_aurora_media_folders/wp_aurora_media_folder_items, distinct from FileBird's ownwp_fbv*tables so both can coexist) rather than importing FileBird's data.- New
MediaFolders.phpmodel (create/rename/delete/reparent, one folder per attachment) +MediaFolderData.phpREST routes (/media-folders,/media-folders/assign), every route double-gated (current_user_can+License::is_feature_available) per this project's established REST-bypass-hardening pattern. Media.vuegains a folder sidebar (MediaFolderTree.vue): create, rename, delete, right-click context menu, drag-and-drop reparenting, and drag-and-drop (or a "Move to folder" fallback) to file media into folders. "All Media" and "Uncategorized" are pinned pseudo-folders.- The Media Library page itself stays free/ungated either way — only the
folder capability is paid. Unlicensed users keep today's flat grid
unchanged; the sidebar shows an inline
UpgradeNoticeinstead of disappearing or locking the whole page — a new gating shape for this codebase (previously: whole sub-app free or whole sub-app paid).
- New
Fixed
Shell::is_own_page()didn't recognizeScreenHijack-based pages (Posts, Pages, Media, Users, Comments — they keep their native URL, no?page=aurora-adminto key off), so it misclassified them as native pages and loadednative-dark.css's own-page exclusion incorrectly, letting that stylesheet's genericacolor rule leak into the real Aurora UI. Invisible until now because no earlier hijacked page rendered a link relying on accent/contrast coloring — Media Folders' new "Enter a license key" CTA in the unlicensed sidebar was the first.ScreenHijack::register()now flagsis_hijack_active()the moment any registered hijack activates for the current request, andis_own_page()checks it.
[2.19.0] - 2026-07-05
Removed
- Remote Site Switcher: the Settings > General "Remote Sites" repeater and
"Remote Site Switcher Capability" field, and the toolbar dropdown they
fed. User asked what it did; investigation found it was inert scaffolding
— it collected and stored a remote site's URL/username/application
password, but nothing anywhere in the codebase ever used the credentials
(no REST calls, no proxying, no SSO). The toolbar "switcher" was just a
plain link opening the remote site's own
/wp-admin/in a new tab. User's call: "doesn't really do much for the software" — removed rather than built out, since a real cross-site integration wasn't the ask. License::PAID_FEATURES["remote-site-switcher"]— the feature's paid gate, now dead code since the feature itself is gone.
Added
Settings::remove_retired_keys()— actively deletes now-unused settings keys from the database oninit, rather than leaving them inertly stored. Added specifically becauseremote_sitesheld a real secret (WordPress application passwords); once the field stopped being read or written, the safer move was to purge it, not just stop touching it.
[2.18.0] - 2026-07-05
Added
- Role Editor: an info icon next to every capability checkbox, with a
hover tooltip explaining what it actually does — user asked directly,
pointing at
level_0/level_1as an example of capabilities with no explanation anywhere in the UI. NewcapabilityDescriptions.jscovers all ~55 WordPress core capabilities (edit_posts,manage_options,unfiltered_html, etc.) plus the legacylevel_0–level_10"user level" system specifically — a pre-2.0 permissions scheme WordPress still assigns to every default role purely for backward compatibility with old themes/plugins that checkcurrent_user_can('level_N')directly instead of a named capability; core itself hasn't checked a level in years. Any capability not in the dictionary (e.g. one added by another plugin) gets a generic "custom capability" fallback rather than showing nothing. Verified live: hoveringlevel_0's info icon on the Subscriber role shows the explanation described above.
[2.17.2] - 2026-07-05
Fixed
AppListShell.vue(the shared master/detail layout used by Database Explorer, Role Editor, Activity Log, Admin Notices, and Menu Creator): the list pane and detail pane shared one page-level scroll instead of scrolling independently. Selecting an item near the bottom of a long list (e.g. Database Explorer's 40 tables) left the detail panel's newly-loaded content rendered off-screen above the current scroll position — looked like nothing happened until you scrolled all the way back up. Root cause: the shell only set amin-heighton the outer flex container, so it just grew with content instead of giving each pane a capped height + its own scrollbar. Fixed by giving the container a realheightand both panesheight: 100%; overflow-y: auto, with a separate mobile-breakpoint treatment (stacked panes get natural height with their ownmax-heightinstead of squashing to 50/50). Verified live: scrolled Database Explorer's table list to the bottom, selectedwp_users, confirmed its data appeared immediately without scrolling.
[2.17.1] - 2026-07-05
Fixed
- File Manager's right-click context menu never appeared for real users
(it worked in earlier automated testing, which turned out to be
triggering the bug's absence rather than confirming its fix). Root
cause: right-clicking — especially via a trackpad's two-finger tap or
Ctrl-click on macOS — can fire a trailing synthetic
clickevent immediately aftercontextmenuon the same target. The menu's "click anywhere else closes it" listener was attached synchronously the moment the menu opened, so that trailing click closed it in the same tick it appeared — a real race condition, not a rendering bug. Fixed by deferring the outside-click listener's registration by one tick (setTimeout(..., 0)) so it only starts listening after the opening event has fully finished. Verified by simulating the exact race (contextmenuimmediately followed byclickon the same element, same tick) and confirming the menu stays open; also confirmed a genuine later click still closes it.
[2.17.0] - 2026-07-05
Added
- File Manager: right-click context menu, mirroring the toolbar exactly (same functions, just a different trigger) so the two can't drift out of sync. Right-clicking an item outside the current selection selects just that item first (standard file-manager behavior); right-clicking within an existing multi-selection leaves it intact so bulk actions still work from the menu. Right-clicking empty space shows folder-level actions (New Folder/File, Upload, Paste) instead. Closes on Escape or a click anywhere else.
- File Manager: the plain
<textarea>editor is now a real code editor (CodeMirror 6 —codemirror+ per-language packages for JS/TS, CSS, HTML, JSON, PHP, Markdown, XML, SQL), with syntax highlighting, line numbers, code folding, and active-line highlighting. Language is chosen by file extension; unrecognized extensions fall back to plain text rather than guessing wrong. Themed directly off Aurora's own CSS variables (not a hardcoded palette) so it matches whatever theme preset is active instead of looking like a foreign widget. Adds real bundle weight (~250KB gzipped) — only loaded when the File Manager page itself is opened, not part of the main admin bundle. - Verified live: context menu appears correctly for both selection and empty-space cases with the right actions enabled/disabled, Escape closes it; PHP syntax highlighting confirmed rendering correctly on a real file (index.php — comments, strings, keywords all colored, doc-block folding visible).
[2.16.0] - 2026-07-05
Changed
- File Manager's actions consolidated into a single elFinder-style toolbar (back/forward/up navigation history, new folder/file, upload, copy/cut/paste, rename, trash, download/edit/info, list/grid view toggle, fullscreen, search) matching the layout of the "WP File Manager" reference plugin, replacing the previous split of header buttons + a per-row Actions column. Selection is now click-to-select (checkbox) + toolbar action, double-click to open a folder or editable file — the toolbar's buttons enable/disable based on what's currently selected (e.g. Rename/Info only with exactly one item selected, Download only for a single file).
- Move/copy switched from a destination-picker modal to clipboard-style
copy/cut/paste — select item(s), click Copy or Cut, navigate anywhere
(tree sidebar, breadcrumb, double-click into a folder), click Paste. A
copy clipboard survives repeated pastes; a cut clipboard clears itself
after the first successful paste. Same
/file-manager/bulkendpoint underneath, just a different frontend trigger. - Added a grid view (thumbnails/icons in a card layout) as an alternative to the list view, and a fullscreen toggle for the File Manager panel itself.
- Verified live: copy → navigate into a folder → paste (file appeared in the new location) → select → trash → confirmed removed; back-navigation and grid view toggle both confirmed working.
[2.15.0] - 2026-07-05
Added
- File Manager closed most of the feature gap against the reference "WP
File Manager" plugin, adding everything that didn't carry the security
risk of the already-excluded zip/unzip and chmod features:
- Move & copy between folders (
copy_item/move_item), with same-containment checks as everything else plus a specific guard against moving/copying a folder into its own descendant. Name collisions at the destination get " (2)", " (3)", etc. appended rather than silently overwriting. - Multi-select + bulk actions — one
/file-manager/bulkendpoint drives trash/move/copy across any number of selected items, reusing the exact same per-item logic as the single-item buttons so bulk behavior can't drift from what those do. Partial failures don't abort the rest — every path gets its own attempt, failures are reported back. - Trash instead of permanent delete — deleting now moves the item
into a flat UUID-named folder under
wp-content/uploads/aurora-admin-trash/plus a JSON manifest (original path, name, type, deleted-at), rather than encoding the original path into the filename (nesting/special characters/length limits make that fragile). Trash view offers restore (recreates parent folders as needed, blocks if something's since taken the original spot), permanent delete, and empty-trash. - Folder tree sidebar — lazy-loaded (only fetches a folder's children when expanded) via the existing list endpoint filtered to directories, no new backend route needed. Reused as the destination picker for move/copy.
- File info panel — read-only permissions/owner/size/dates/mime type
via a new
/file-manager/infoendpoint; directory size is computed on-demand for this one-off request rather than for every row in the main list (would slow down browsing large folders for no benefit). - Image thumbnails — a new
/file-manager/previewendpoint serves image files inline (not as an attachment) for use as an<img>src; no server-side resizing, just the extension allowlist + size cap every other endpoint here already uses. - Drag-and-drop upload (multiple files at once) and sortable columns (Name/Size/Modified, folders always first regardless of sort) — both frontend-only, no new backend surface.
- Aurora's own trash folder is hidden from the normal browser view when
listing
wp-content/uploads— it's an implementation detail, not a real user-created folder. - Verified live: multi-select, info panel, and the full create → trash → restore round trip through the UI; copy/bulk-trash/empty-trash verified directly against the REST endpoints.
- Move & copy between folders (
- Still deliberately not built (unchanged from the original scope decision): zip/unzip (zip-slip risk) and chmod/permissions editing (too easy to lock yourself out).
[2.14.0] - 2026-07-05
Added
- Developer-only license override for local testing — lets Pro
features be tested without buying a license against the developer's own
software. Settings > Developer's "Simulate active license" toggle treats
every paid feature as fully licensed, bypassing both the real license
check and the file-integrity tamper check. Two gates keep this from ever
doing anything on a real install even if this code ships by accident:
(1) it only has any effect at all when
AURORA_ADMIN_DEV_MODEis defined truthy inwp-config.php— never set anywhere in this repo or the release zip, only in a local dev environment's own config — and (2) the Settings > Developer category itself only renders in the UI when that constant is present. This entire feature (theDEV_MODE_CONSTANTchecks inLicense.php, thedevModeAvailableflag inSettingsPage.php, and the "Developer" category insettingsConfig.js/Settings.vue) must be removed before packaging a release build — every touch point is markedDEV ONLYin comments. Verified live: toggling it on/off correctly unlocks/re-locks a Pro feature (Role Editor) end-to-end, including the sidebar's lock badge.
[2.13.1] - 2026-07-05
Fixed
- Every
ScreenHijack-mounted page (Posts, Pages, Media, Users, Comments, the 5 native Settings screens, Media Upload, Add New User, Nav Menus, Themes, Widgets, Profile) was missing the traffic-light title bar and rendering outside the normal card frame — the DOM structure didn't match native/other Aurora pages. Root cause: WordPress prints<div id="wpbody"><div id="wpbody-content">after thein_admin_headerhook fires (confirmed directly in core'sadmin-header.php), butScreenHijack.phpstarted its output buffer onin_admin_header— so those wrapper divs' opening tags, and later their closing tags (printed beforeadmin_footerfires), 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 brokewindowChrome.js's#wpbody-content-based traffic-light insertion and the shell's card framing CSS. Fixed by havingScreenHijack.phpre-print the#wpbody/#wpbody-contentwrapper 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 framingwindowChrome.js/shell-frame.cssalready had working (traffic-light dots overlaid on the editor). Added a "← Posts"/"← Pages" breadcrumb next to the dots, resolved from thepost-type-{slug}body class WP core already adds to every post edit screen (needed since Posts/Pages moved off a fixedadmin.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 sameblock-editor-pagebody class the breadcrumb logic keyed off, but has nopost-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 nopost-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.
- Caught and fixed a real bug while testing: the Site Editor
(
[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/meREST route, plus a small newProfileData.phproute for password changes (core deliberately excludes password from its own REST schema). Free, gated bymodern_users.- Deliberately does not attempt to host third-party plugin fields (2FA
setup, WooCommerce customer fields, etc., added via the
show_user_profile/personal_optionshooks) — a Vue reskin has no safe way to render another plugin's raw HTML/JS. Instead, the hijack checkshas_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 hookingshow_user_profileand 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.
- Deliberately does not attempt to host third-party plugin fields (2FA
setup, WooCommerce customer fields, etc., added via the
[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_takeovermaster toggle and the"admin-takeover"paid feature; every new REST controller checks the license directly in its ownpermission_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, andNavMenus.vue's fetch calls didn't checkres.okbefore 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/mediaand/wp/v2/usersREST routes, which already cover file upload and user creation. Free, gated by the samemodern_media/modern_userstoggles 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/settingsREST route; Permalinks has no core REST route for rewrite rules, so it gets its own smallPermalinksData.phpcontroller 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 checksLicense::is_feature_available("admin-takeover")directly in its ownpermission_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:readonlysupport on text fields (Site Address shouldn't be editable inline) and aselectfield 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 viaHijackedNativePages.php), the same approach the original uixpress reference plugin used. WordPress'sload-{page}.phphook 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.phpprints 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 noredirect_native_pages()case for direct visits toplugins.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-contentid no matter how many classes are stacked on it — the exact same CSS-specificity trap as an earlier block-editor background bug. Added!importantto 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, samemanage_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 viarealpath()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.
- Every path a client sends is resolved to a real,
Fixed
- File Manager's REST endpoints now also check
License::is_feature_available('file-manager'), not justmanage_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=pageredirects; 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.phpredirects;user-new.php(account creation) is untouched. - All four gated by their own Settings toggle (
modern_pages,modern_media,modern_users—modern_postsalready existed), each defaulting to on.
- Pages (
[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 nativeedit.phplist table. NewPostsData.phpREST 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_commentssettings 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 newmodern_posts.
[2.4.2] - 2026-07-04
Fixed
- Admin Notices only ever showed on the Aurora Dashboard — everywhere
else,
admin_noticesfired normally, but on the Dashboard specifically the notice was invisible:DashboardTakeoverhides#wpbodyentirely there (to hide the native dashboard, not our own), and that's exactly where WordPress printsadmin_noticesoutput. 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.jsunconditionally relocates every.noticeelement to right after the page's<h1>) using core's own documentedbelow-h2opt-out class, since without it the notice would've just been silently moved back into the hidden#wpbodyby 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-framenever got the height cap + internal scroll treatment#wpbody-contentgot in an earlier fix; combined withhtml/bodybeing locked tooverflow: 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_mimesallowlist and the real-mime sniff that nulls out the file type for SVGs specifically), plus a PHP warning (wp_get_missing_image_subsizes()reading undefinedwidth/heightmetadata 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<script>tags, inline event handler attributes,javascript:URIs) since SVG can carry executable content.
[2.2.0] - 2026-07-04
Added
- Settings > Login's "Style login page" and "Login logo" fields now
actually do something — previously they existed in the UI with no
implementation behind them at all. Styles
wp-login.phpwith a dark theme (background, card, inputs, button) and swaps in the configured logo image in place of the WordPress logo, linked to the site's home URL instead of wordpress.org.
[2.1.1] - 2026-07-04
Fixed
- Image fields (Logo, Dark mode logo, Login logo, Admin favicon) now open
the real WordPress media library picker.
wp_enqueue_media()was never called, sowp.mediawas undefined client-side and every image field silently fell back to a plain URL prompt instead.
[2.1.0] - 2026-07-04
Added
- Free/paid feature gating: Role Editor, Activity Log, Database Explorer, Analytics, White Label, and the remote site switcher are now Pro features. Menu Creator, Admin Notices, Comments, Plugins, and the core shell/theming stay free. Locked pages show an upgrade prompt instead of the real feature; the sidebar shows a lock badge on locked items.
- Real license validation against the
auroraplugins.comlicense server (validate/activate/deactivate), replacing the earlier placeholder that treated any non-empty key as active. Status is cached for 12 hours and re-checked immediately on a license-key change. The Settings page shows a live status line (active/invalid/unreachable) under the License key field. - File-integrity tamper protection: if the code implementing the license gate has been modified, every paid feature re-locks regardless of an otherwise-valid license — makes stripping the check out of a redistributed copy not actually unlock anything.
Fixed
- Role Editor's REST endpoint 500'd when hit via the REST API — it called
get_editable_roles(), which WordPress only auto-loads for normal wp-admin page requests, not REST requests.