== Changelog == = 1.2.0 = _Release date: 2026-08-15_ **Highlights** * Subscription status, visible at a glance (Phase 4). The catalog now pulls the account's subscriptions from Core (`GET /me/subscriptions`, cached for one hour; refreshed on catalog refresh and key change) and shows a pill next to each premium plugin's price: Subscribed (with "N days left" when expiring within 14 days), Payment failed, Cancelled, or Expired. Admin notices at the top of the page warn about failed payments, subscriptions expiring within 14 days, and expired subscriptions whose plugin is still active on this site. **Changed** * Plugin version 1.1.0 → 1.2.0. No database schema changes (no custom tables). **Compatibility** * WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4) * PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit) = 1.1.0 = _Release date: 2026-08-15_ **Added** * Cascade dependency install: when installing a plugin whose catalog entry declares `requires_plugins` (Core 1.7.0+), Manager first installs every missing dependency — ecosystem plugins through the store flow, WordPress.org plugins (e.g. Action Scheduler) via `plugins_api` and their repository ZIPs — dependencies of dependencies first, cycle-safe. The success notice lists what was installed; failures name the dependency and abort before the main install. Catalog rows show a "Requires: …" hint in the detail line. **Changed** * Plugin version 1.0.0 → 1.1.0. No database schema changes (no custom tables). **Compatibility** * WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4) * PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit) = 1.0.0 = _Release date: 2026-08-15_ **Highlights** * Catalog page redesign. The table columns are now Plugin · Version · Requires WP · Requires PHP · Price · Action · Status: the Type column is merged into Price ("Free" for cost-0 plugins, the annual price for premium), the version moved into its own column, the action buttons sit before the status, and "Local state" is renamed "Status". Each plugin's detail row — website link first, then its description — is now **always open** (the click-to-expand toggle is gone). An intro paragraph after the page title explains the store, and Support + Payments sections below the table describe how to get help and how the annual-subscription model works (automatic renewal, cancellation from the account page, Mollie processing). **Changed** * Plugin version 0.6.0 → 1.0.0 — first stable release. No database schema changes (no custom tables). **Compatibility** * WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4) * PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit) = 0.6.0 = _Release date: 2026-08-15_ **Added** * Spanish (es_ES) and Catalan (ca) translations — all 73 admin strings, regenerated POT included. * "Refresh catalog" is rate-limited to 6 refreshes per minute per user (transient bucket), redirecting back with an error notice when exceeded; the limit rejects before clearing any cache (Phase 6 hardening). **Changed** * Plugin version 0.5.3 → 0.6.0. No database schema changes (no custom tables). **Compatibility** * WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4) * PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit) = 0.5.3 = _Release date: 2026-08-15_ **Added** * Authenticated encryption for the stored API key (same encrypt-then-MAC scheme as Core 1.6.0): tampered payloads fail closed, and encryption refuses to run without real WordPress salts. Legacy-stored keys keep decrypting and re-encrypt on the next save. **Changed** * Plugin version 0.5.2 → 0.5.3. No database schema changes (no custom tables). **Compatibility** * WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4) * PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit) = 0.5.2 = _Release date: 2026-08-15_ **Fixed** * Manager-panel Install/Update failed while the native WordPress updater worked. Two defects in the panel's upgrader path (`Robotstxt_Manager_Installer`): 1. `ensure_plugin_functions()` required only `class-wp-upgrader.php`, which since the WordPress 5.3 class split no longer defines `Plugin_Upgrader` (it lives in `class-plugin-upgrader.php`). In a stock `admin-post.php` context nothing else loads it → fatal error. Both files are now required. 2. The overwrite flag was passed to `Plugin_Upgrader::install()` as `'overwrite'`, but current WordPress reads `'overwrite_package'` — the unknown key was silently discarded, `clear_destination` stayed false, and the install failed against the existing folder. Both keys are now passed (the unused one is ignored by `wp_parse_args()`), keeping compatibility across WP versions. * Verified end-to-end on the live store: panel-path download → overwrite-install succeeds, plugin remains active. **Changed** * Plugin version 0.5.1 → 0.5.2. No database schema changes (no custom tables). **Compatibility** * WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4) * PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit) = 0.5.1 = _Release date: 2026-08-15_ **Highlights** * Stabilization release after a full code + security audit (fresh-context review per AGENTS-deploy.md) and full-range compatibility scans (PHPCompatibility 5.6–8.5, wp-compat laddering). Declared requirements now match the real floors: **WordPress 4.4+** (was 4.7) and **PHP 8.0+** (was 7.4 — the updater's `mixed` type hints and `str_contains()` require 8.0; previously under-declared, which would have been a fatal on 7.4). **Fixed** * `test_connection()` reported "Connected." on non-200 responses and `get_catalog()` cached auth failures as an empty catalog for the full TTL. Both now check the HTTP status; non-200 responses return an error/empty and are never cached. * `site_domain()` used `ltrim( $host, 'www.' )`, which strips a character set and mangles hosts starting with `w` (e.g. `webdev.example.com` → `ebdev…`, breaking premium package URLs). Now strips only the literal `www.` prefix. * Native-update integration no longer injects premium `response` entries when no decryptable API key exists (the native updater would download into a 403). * Opt-in uninstall: also deletes the `update_plugins` site transient (premium entries embed the API key in the package URL — the plaintext copy must not outlive the plugin) and stops deleting a phantom `robotstxt_manager_db_version` option nothing ever wrote. **Changed** * API-key setting validates the format (UUID-like, 8–127 chars) before storing, rejecting mangled input at save time instead of failing later at connection time. * Plugin version 0.5.0 → 0.5.1. No database schema changes (no custom tables). **Compatibility** * WordPress: 4.4 - 7.1 (scan-verified: wp-compat clean from 4.4) * PHP: 8.0 - 8.5 (scan-verified: PHPCompatibility + manual feature audit) = 0.5.0 = _Release date: 2026-08-14_ **Highlights** * Native WordPress update integration: installed catalog plugins feed into WordPress's own update system. The standard "Update available" badge appears on the Plugins screen, and clicking "Update now" runs the normal WordPress updater with the ROBOTSTXT store as the download source — no custom UI. **Added** * `Robotstxt_Manager_Updater` — injects catalog update data into the `update_plugins` transient (`pre_set_site_transient_update_plugins`): a `response` entry for outdated catalog plugins (package = the store's download proxy; premium entries append the account API key as `api_key`, which Core 1.5.0+ accepts as a query parameter because the native upgrader cannot send headers) and a `no_update` entry for up-to-date ones. Entries injected by a plugin's own bundled SDK are never overwritten. * `plugins_api` filter — the "View details" modal for catalog slugs is served from catalog data (name, version, requires, tested, homepage, description section, icon, banner). * "Refresh catalog" now also deletes the `update_plugins` transient so the update badges re-evaluate immediately. **Changed** * Plugin version 0.4.0 → 0.5.0. No database schema changes (no custom tables). **Compatibility** * WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower) * PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower) = 0.4.0 = _Release date: 2026-08-14_ **Highlights** * Click-to-expand rows in the catalog table: clicking the ▸ next to a plugin's name reveals a detail row with the plugin's description and a link to its website on the store, so users can understand what each plugin does before installing. Pure CSS toggle (`:has()` + hidden checkbox) — no JavaScript, consistent with the classic-flow philosophy. **Added** * Detail row under each catalog entry, shown when the entry has a description or a website URL (Core 1.4.2+ exposes `description` in the catalog list; entries without either keep a plain, non-expandable row). * "Visit website" link using `page_url` (falling back to `homepage`), opened in a new tab with `rel="noopener noreferrer"`. **Changed** * Plugin version 0.3.1 → 0.4.0. No database schema changes (no custom tables). **Compatibility** * WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower) * PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower) = 0.3.1 = _Release date: 2026-08-14_ **Changed** * Default store URL is now `https://www.robotstxt.software` (was `https://plugins.robotstxt.es`). Applies to new activations and fresh installs; existing saved URLs are preserved. The catalog API at the new domain is verified live. * Plugin version 0.3.0 → 0.3.1. **Compatibility** * WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower) * PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower) = 0.3.0 = _Release date: 2026-08-13_ **Highlights** * Classic install/activate/update flow: the catalog actions now run on a full page load (`admin-post.php` with per-action-and-slug nonces) and report the outcome through standard admin notices (green success / red error) at the top of the page, replacing the previous AJAX flow. **Added** * Activate action — installed-but-inactive plugins show an "Activate" button that calls `activate_plugins()`. * Update action — plugins with a newer catalog version show an "Update" button that re-downloads the ZIP and runs the `Plugin_Upgrader` with `overwrite` enabled. * Install/Activate/Update buttons are nonce links (`robotstxt_manager_{action}_{slug}` nonce actions) to `admin-post.php`, all `manage_options`-gated. * Redirect-based notices: handlers redirect back to the catalog page carrying `robotstxt_manager_result` (success/error) and `robotstxt_manager_message`, rendered as dismissible standard notices. **Changed** * `Robotstxt_Manager_Installer` rewritten from an AJAX handler to three admin-post handlers (`robotstxt_manager_install`, `robotstxt_manager_activate`, `robotstxt_manager_update`); download logic (Bearer account-key auth, ZIP validation, temp-file cleanup) unchanged. * Removed the AJAX endpoint, `manager-install.js`, and its script localization. * Plugin version 0.2.0 → 0.3.0. **Compatibility** * WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower) * PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower) = 0.2.0 = _Release date: 2026-08-12_ **Highlights** * Phase 2: the Install action. Free and premium plugins can be installed directly from the ROBOTSTXT catalog into the local site, authenticated with the account-level API key (requires Plugins Core 1.4.0+). **Added** * `Robotstxt_Manager_Installer` — AJAX handler (`wp_ajax_robotstxt_manager_install_plugin`, nonce-protected, `manage_options`-gated) that resolves the plugin in the remote catalog, downloads the ZIP (free plugins with a published public `download_url` directly; everything else via Core's authenticated `/download` endpoint with an `Authorization: Bearer ` header), validates the archive (ZIP magic bytes), installs it via `Plugin_Upgrader`, and cleans up the temp file on every path. * `public/js/manager-install.js` — wires the Install buttons to the AJAX action with in-flight state and result rendering. * Catalog view: enabled Install buttons for compatible plugins (free and premium). **Changed** * Plugin version 0.1.3 → 0.2.0. Description updated to reflect Phase 2. **Compatibility** * WordPress: 7.0 - 7.1 (declared floor of the ecosystem; scan-confirmed lower) * PHP: 8.4 - 8.5 (declared floor of the ecosystem; scan-confirmed lower) = 0.1.3 = _Release date: 2026-08-12_ **Changed** * Version bumped 0.1.2 → 0.1.3. * `composer.json` PHP requirement updated from `>=8.4` to `>=7.4` to match the real PHPCompatibility scan floor and the plugin header. * `composer.lock` regenerated. * `.pot` file regenerated for v0.1.3. **Compatibility** * WordPress: 4.7 - 7.1 * PHP: 7.4 - 8.5 **Tests** * PHP Coding Standards: PHP_CodeSniffer 3.13.6 * WordPress Coding Standards: WPCS 3.4.1 * PHPStan: level 9, 0 errors * PHPCompatibility: 7.4–8.5 clean * wp-compat: 0 errors against WP 4.7 = 0.1.2 = _Release date: 2026-08-12_ **Highlights** * Documentation refresh: readme.txt and changelog.txt aligned to the standard templates. **Changed** * Version bumped 0.1.1 → 0.1.2. * readme.txt: added Extra Configurations section, Automatic download subsection in Installation, Previous versions link in Changelog. * changelog.txt: 0.1.1 entry reformatted to follow the standard template (Highlights, Changed, Compatibility, Tests). * PHPCompatibility scan (5.6–8.5): real PHP floor is **7.2**. Project declares **8.4** (ecosystem exception). * wp-compat scan against WordPress 4.7: **zero WPCompat errors**. Real WP floor is **≤ 4.7**. Project declares **7.0** (ecosystem support window). **Compatibility** * WordPress: 4.7 - 7.1 * PHP: 7.4 - 8.5 **Tests** * PHP Coding Standards: PHP_CodeSniffer 3.13.6 * WordPress Coding Standards: WPCS 3.4.1 * PHPStan: level 9, 0 errors * PHPCompatibility: 8.4–8.5 clean (real floor 7.2) * wp-compat: 0 errors against WP 4.7 = 0.1.1 = _Release date: 2026-08-12_ **Highlights** * Compatibility scans completed and documented. No code changes. **Changed** * Version bumped 0.1.0 → 0.1.1. * PHPCompatibility scan (5.6–8.5) completed: real PHP floor is **7.2** (highest-required feature is `object` return type, introduced in PHP 7.2). The project deliberately declares **8.4** to match the ROBOTSTXT ecosystem — this is a documented exception to `AGENTS-deploy.md` §"PHP compatibility check". * wp-compat scan against WordPress 4.7: **zero WPCompat errors**. The Manager's code uses only WordPress APIs available since 4.7 or earlier. The real WordPress floor is **≤ 4.7**; the project declares `Requires at least: 7.0` to match the ecosystem support window. * `.pot` file regenerated for v0.1.1. **Compatibility** * WordPress: 4.7 - 7.1 * PHP: 7.4 - 8.5 **Tests** * PHP Coding Standards: PHP_CodeSniffer 3.13.6 * WordPress Coding Standards: WPCS 3.4.1 * PHPStan: level 9, 0 errors * PHPCompatibility: 8.4–8.5 clean (real floor 7.2) * wp-compat: 0 errors against WP 4.7 = 0.1.0 = _Release date: 2026-08-12_ **Highlights** * Phase 1 scaffold of Manager (by ROBOTSTXT) — client-side dashboard for the ROBOTSTXT plugin ecosystem. Read-only catalog view with local install-state resolution. **Added** * Plugin header, autoloader (Composer with manual fallback), lifecycle hooks, constants (`ROBOTSTXT_MANAGER_VERSION`, `ROBOTSTXT_MANAGER_DIR`, `ROBOTSTXT_MANAGER_URL`, `ROBOTSTXT_MANAGER_BASENAME`). * `Robotstxt_Manager_Loader` action/filter queue (identical pattern to Core/Mollie/Sync). * `Robotstxt_Manager_Encryption` — AES-256-CBC encrypt/decrypt for the API key, using WordPress `AUTH_KEY` + `AUTH_SALT` for key derivation. Same pattern as Core's Forgejo token encryption. * `Robotstxt_Manager_Core_Client` — HTTP client for the remote Plugins Core REST API. Authenticated via `Authorization: Bearer ` header. Implements `test_connection()`, `get_catalog()` (cached in transient), `clear_catalog_cache()`. * `Robotstxt_Manager_Settings` — Settings API page with Store URL, API Key (masked, encrypted, last-4 hint, delete-on-empty preservation), Catalog Cache TTL, Data-on-Uninstall opt-in. AJAX "Test connection" button with nonce + capability check. * `Robotstxt_Manager_Admin` — Top-level admin menu (label "ROBOTSTXT") with catalog table view. Resolves local install/active/update state for every catalog entry using `get_plugins()` + `is_plugin_active()`. "Refresh catalog" admin-post action. * `uninstall.php` with opt-in data deletion (default: preserve all options and transients). * Per-project tooling: Composer, PHPCS + WPCS + PHPCompatibility, PHPStan level 9 + wp-compat, PHPUnit. `bin/preflight.sh` and `bin/deploy.sh`. **Compatibility** * WordPress: 4.7 - 7.1 * PHP: 7.4 - 8.5 **Tests** * Phase 1 scaffold — plugin-header tests included. Additional tests for Core client, settings, and admin rendering in follow-up phases.