diff --git a/CHANGELOG-legacy.md b/CHANGELOG-legacy.md new file mode 100644 index 0000000..527b27b --- /dev/null +++ b/CHANGELOG-legacy.md @@ -0,0 +1,1293 @@ +# 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 `` markup + into a `` 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 + `` 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 [--days=] + [--max-sites=]` 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 `