Files
aurora-wordpress-admin/CHANGELOG-legacy.md
T
NixReaperandClaude Sonnet 5 24be114f9a Advance full admin takeover from v1.1 to v1.12
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]>
2026-07-26 13:37:35 -05:00

75 KiB
Raw Blame History

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:ignore fixes, found via another fresh Plugin Check report after 3.5.1's fix. Turns out WordPress.DB.DirectDatabaseQuery.DirectQuery/NoCaching and WordPress.DB.PreparedSQL.NotPrepared/InterpolatedNotPrepared are attributed by PHPCS to different lines within the same multi-line $wpdb->get_results()/get_row() call — DirectQuery/NoCaching to the outer call's opening line, NotPrepared to 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 (fixing NotPrepared) but broke coverage of the outer line (DirectQuery/NoCaching came back) for four calls in MenuCreatorData.php and Notices.php/NoticesData.php that 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 namespace aurora-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 standalone Options\Settings class was added, backed by its own aurora_site_backup_settings option — same minimal shape as Aurora Admin's own Settings class, just a separate store.
    • CompanionPluginData.php's PLUGINS config array (already generalized in 3.5.0) gained a site-backup entry — 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.zip is not yet hosted at https://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).

[3.5.1] - 2026-07-11

Fixed

  • Misplaced phpcs:ignore comments from the 3.3.2 compliance pass, found via a fresh Plugin Check report. WPCS's phpcs:ignore suppresses 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 across MenuCreatorData.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 a MediaFolders::name_taken() helper to replace an inline while() condition that couldn't otherwise be annotated), DbDumper.php, AnalyticsData.php, and MediaData.php's post__not_in VIP-advisory comment. Also added a genuinely missing annotation on Analytics::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 as new \WP_REST_Request("GET", "", ["id" => $wpdb->insert_id]) — but WP_REST_Request's third constructor argument doesn't populate what get_param() reads, so get_one() always received a null id and returned "Menu not found" instead of the newly created menu. Confirmed via a full rest_do_request() dispatch (not just a direct function call) that this reproduced through the real REST route. Fixed by building the request with set_url_params(), WP core's own documented pattern for this.
  • Confirmed the compressed_files/hidden_files/unexpected_markdown_file findings 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 via unzip -l that none of those paths exist in wporg-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.php and its Vue UI (plus the shared AppListShell/AppListItem list-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 namespace aurora-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.php was generalized from a single file-manager-only class into a small PLUGINS config array keyed by slug, so both File Manager and Database Explorer (and any future companion plugin) share the same install/activate/status REST routes and Plugin_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.php and 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 namespace aurora-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 to theme-editor.php now fall through to WordPress core's own Theme Editor unmodified, which already respects DISALLOW_FILE_EDIT itself.
    • 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's Stable tag had 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 an empty()/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's defined("ABSPATH") || exit() guard sat after 49 use import 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 the namespace declaration, this file's one deliberate departure from the rest of the codebase's "guard after imports" convention.
    • Roughly 230 WordPress.DB.DirectDatabaseQuery/PreparedSQL/ UnescapedDBParameter findings 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 justified phpcs:ignore the 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-annotated fopen() 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 RuntimeException messages 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) and WP_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.

[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_options user open and overwrite existing .php files in-place with immediate execution effect — no different in risk from WordPress core's own Theme/Plugin editor, which core lets hosts disable via DISALLOW_FILE_EDIT specifically 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 matching BLOCKED_EXTENSIONS (php, phtml, cgi, py, sh, and others) is refused. File Manager also now honors DISALLOW_FILE_EDIT (blocks all editing) and DISALLOW_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 and Range-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 a wp_cron safety 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 under wp-content/ (excluding the backup storage directory itself and, structurally, wp-config.php, which lives outside wp-content/ entirely), zipped/extracted in bounded batches via ZipArchive, 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 own index.php/.htaccess stub pair; the real protection is that every archive read goes through a manage_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.php for 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.
  • Live-verified end-to-end on the dev site, including catching and fixing two real bugs along the way: a job's phase column 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 URI and Author URI were both https://auroradragon.studio, and WP.org requires them to be distinct (one identifies the plugin, the other the author). Plugin URI now points to https://aurora.auroradragon.studio (the plugin's own marketing site); Author URI stays 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 forbidden wp_get_sidebars_widgets() (internal-only core function) with a local helper reading the sidebars_widgets option directly.
    • FileManagerData.php: converted every raw filesystem call (move_uploaded_file(), mkdir(), rename(), unlink(), rmdir(), is_writable(), readfile()) to the WP_Filesystem API, which works across hosting setups that don't allow direct filesystem access. move_uploaded_file()'s upload-tampering check is preserved via an explicit is_uploaded_file() call before handing off to WP_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, and ActivityLogData.php with justified phpcs:ignore comments — 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) and bin/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_key and dev_license_override are 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.com service (beta licenses, Pro purchases) — that server-side system is untouched and unaffected by this change, it's simply no longer wired into this codebase.

[2.27.0] - 2026-07-06

Added

  • Restored the dev-only license override (AURORA_ADMIN_DEV_MODE wp-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 in PROJECT_STATUS.md. Only has any effect when AURORA_ADMIN_DEV_MODE is defined truthy in a given install's own wp-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 via SubApps.php like 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 new POST aurora-admin/v1/bug-report REST route (admin/src/Rest/BugReportData.php), which forwards to a new License::submit_bug_report() method that calls the license server.
    • License server (aurora-licensing plugin, not this repo): added a wp_aurora_bug_reports table (DB version 3 → 4, same version-gated migration idiom) and a new public POST /report-bug REST route, storing the description plus whatever license key/domain/diagnostics fields were sent. Deliberately a plain wpdb->insert with no admin UI yet — reports are reviewable via wp db query for 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_reports on the production server, then deleted the two test rows (one from an earlier curl smoke test) by exact ID.

[2.25.0] - 2026-07-05

Removed

  • Dev-mode license override (AURORA_ADMIN_DEV_MODE wp-config.php constant, Settings > Developer category's toggle, and every call-site in License::is_active()/is_feature_available()) — this was a build-time-only escape hatch for testing paid features without a live license, marked in PROJECT_STATUS.md as "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 on License::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-licensing plugin on the live auroraplugins.com box, not this repo): added an is_beta column to wp_aurora_licenses (migrated in place via a plugins_loaded-hooked check, since the file was updated without re-activation — same version-gated migration idiom this plugin's own ActivityLog.php etc. use), a wp 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), and is_beta added to /validate and /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's is_beta flag (already flowing through the existing status() 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-functional is_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_updated flow).

[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 Category and Default Post Format are now real dropdowns (populated from get_categories()/ get_post_format_strings()) instead of raw numeric IDs. Added use_smilies — registered in core's own REST settings schema but never actually added to this screen.
    • Reading: Homepage/Posts Page are now page-title dropdowns (via get_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 generic showIf field property in NativeSettings.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 raw get_option()/ update_option() reads/writes against an explicit key allowlist (same pattern as PermalinksData.php, generalized to many keys instead of one).
    • New textarea field type in SettingsField.vue for 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.

[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's delete_plugins(), which internally calls request_filesystem_credentials() (WP_Filesystem setup) — that function lives in wp-admin/includes/file.php, which a REST API request doesn't load the way a normal wp-admin page load does, and delete_plugin() only required plugin.php. update_plugin() right below it already required file.php for 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 $submenu global) — 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_file global, set directly in each of these pages' own PHP (theme-install.php sets $parent_file = 'themes.php', media-new.php sets 'upload.php', user-new.php sets '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 as activeParentFile; Sidebar.vue's isSectionActive() 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.

[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-overlay specifically is touched, not .wp-full-overlay generally) is reframed to Aurora's standard inset box: the overlay itself plus its two independently position: fixed children (.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-left on .expanded/.collapsed mirrors 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 sets body.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 own height: 100% (WP core) combined with our top+bottom insets was the exact same over-constrained-box bug already known from the block editor's .interface-interface-skeleton — same height: auto !important fix.
    • The traffic-light winbar needed a permanently-running MutationObserver rather 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's load-themes.php hijack never passed a link to theme-install.php (only customizeUrl), so there was no way to reach either from Aurora's Themes page. Themes.vue gains an "Add New Theme" button (next to Customize) linking to native theme-install.php, gated on current_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's newMediaUrl (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, so native-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.json and rebuilt dist/aurora-admin-v2.zip from a fresh staging copy — the protected files' checksums (Integrity::is_tampered()'s manifest) had gone stale after this session's edits to aurora-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 own wp_fbv* tables so both can coexist) rather than importing FileBird's data.
    • New MediaFolders.php model (create/rename/delete/reparent, one folder per attachment) + MediaFolderData.php REST 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.vue gains 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 UpgradeNotice instead of disappearing or locking the whole page — a new gating shape for this codebase (previously: whole sub-app free or whole sub-app paid).

Fixed

  • Shell::is_own_page() didn't recognize ScreenHijack-based pages (Posts, Pages, Media, Users, Comments — they keep their native URL, no ?page=aurora-admin to key off), so it misclassified them as native pages and loaded native-dark.css's own-page exclusion incorrectly, letting that stylesheet's generic a color 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 flags is_hijack_active() the moment any registered hijack activates for the current request, and is_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 on init, rather than leaving them inertly stored. Added specifically because remote_sites held 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_1 as an example of capabilities with no explanation anywhere in the UI. New capabilityDescriptions.js covers all ~55 WordPress core capabilities (edit_posts, manage_options, unfiltered_html, etc.) plus the legacy level_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 check current_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: hovering level_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 a min-height on 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 real height and both panes height: 100%; overflow-y: auto, with a separate mobile-breakpoint treatment (stacked panes get natural height with their own max-height instead of squashing to 50/50). Verified live: scrolled Database Explorer's table list to the bottom, selected wp_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 click event immediately after contextmenu on 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 (contextmenu immediately followed by click on 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/bulk endpoint 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/bulk endpoint 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/info endpoint; 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/preview endpoint 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.
  • 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_MODE is defined truthy in wp-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 (the DEV_MODE_CONSTANT checks in License.php, the devModeAvailable flag in SettingsPage.php, and the "Developer" category in settingsConfig.js/Settings.vue) must be removed before packaging a release build — every touch point is marked DEV ONLY in 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 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 <h1>) 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 <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.php with 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, so wp.media was 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.com license 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.