From 6c6a7fca00a4adb43885dbafba50eaa4ce0aafab Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Sun, 9 Aug 2026 08:15:09 +0000 Subject: [PATCH 1/3] v1.8.2 --- changelog.txt | 33 +++++++++++++++++++++++ readme.txt | 57 ++++++++++++++-------------------------- robotstxt-mediaaudit.php | 6 ++--- update.json | 27 +++++++++++++++++++ 4 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 update.json diff --git a/changelog.txt b/changelog.txt index f0a89d7..d07cf8e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,38 @@ == Changelog == += 1.8.2 = + +_Release date: 2026-08-08_ + +**Changed** + +* Contribution guidelines restructured: `AGENTS.md` is now a slim index pointing to topic-specific `AGENTS-*.md` files (single source of truth — the security checklist and the audit prompt template each live in exactly one canonical file). +* `bin/deploy.sh` now reads its release exclude list from a shared `bin/dist-exclude.txt` so staging and verification cannot drift apart. + +**Added** + +* `bin/preflight.sh` automated pre-deploy verification script (PHPCS, PHPStan level 9, PHPCompatibility, PHPUnit, `composer audit`, candidate-ZIP artifact inspection). +* Plugin header tests (`tests/`) validating required/forbidden headers and `Stable tag` ↔ `Version` ↔ `ROBOTSTXT_MEDIAAUDIT_VERSION` consistency. +* `.claude/settings.json` deny rules mechanically enforcing the no-auto-deploy / no-push / no-tag / no-merge boundary. +* `.claude/agents/security-auditor.md` (read-only pre-deploy audit subagent) and `.claude/commands/deploy-check.md` slash command. + +**Fixed** + +* `bin/deploy.sh` now excludes all `AGENTS-*.md` dev docs from the release ZIP; previously only `AGENTS.md` was excluded, so the split topic files would have leaked into the distribution. + +**Compatibility** + +* WordPress: 5.3 - 7.1 +* PHP: 8.0 - 8.5 +* WP-CLI: 2.x + +**Tests** + +* PHPCS (WordPress-Core, WordPress-Docs, WordPress-Extra): PASS +* PHPStan level 9: PASS +* PHPCompatibility 8.0-8.5: PASS +* PHPUnit: 8 tests, 41 assertions + = 1.8.1 = _Release date: 2026-06-08_ diff --git a/readme.txt b/readme.txt index 2754e45..4ae0042 100644 --- a/readme.txt +++ b/readme.txt @@ -2,10 +2,10 @@ Contributors: javiercasares, robotstxt Tags: media, copyright, images, reverse image search, media library Requires at least: 5.3 -Tested up to: 7.0 +Tested up to: 7.1 Requires PHP: 8.0 Requires Plugins: action-scheduler -Stable tag: 1.8.1 +Stable tag: 1.8.2 License: GPL-3.0-or-later License URI: https://www.gnu.org/licenses/gpl-3.0.txt @@ -90,8 +90,8 @@ By default, no. Enable **Settings → Delete data on uninstall** if you want all == Compatibility == -* WordPress: 6.8 - 7.0 -* PHP: 8.2 - 8.4 +* WordPress: 5.3 - 7.1 +* PHP: 8.0 - 8.5 * WP-CLI: 2.x * Requires Plugins: action-scheduler @@ -99,44 +99,27 @@ By default, no. Enable **Settings → Delete data on uninstall** if you want all Only the 3 latest versions. The full changelog is in [changelog.txt](changelog.txt). -= 1.6.0 = += 1.8.2 = -* Settings page reorganised into four tabs: General, API Credentials, Filters, External Scanning. -* New Filters tab: configurable Alert Hostnames and Ignored Hostnames lists with wildcard support (`*.example.com`), auto-normalization (strips scheme/path/port/www., deduplicates, sorts), and default pre-populated lists (60 alert domains, 56 ignored domains) seeded on first activation. -* New "Alert" status filter in the audit list and alert badge in the External Status column. -* Dashboard stats: new "Alert" card showing attachments with at least one alert domain match. -* Quick-view modal: ignored domains filtered out; alert domains highlighted with a status badge. -* Full Report detail page: external results split into three sections — Alert, Other, and Ignored domains. -* Unified CSV export replacing separate internal/external exports: "Export CSV" (all) and "Export CSV (Alerts only)", one row per attachment with all internal and external data including per-provider match counts and classified domain lists. -* "Media Audit" admin menu repositioned immediately below the built-in Media menu. +* Contribution guidelines restructured: `AGENTS.md` is now an index to topic-specific `AGENTS-*.md` files (single source of truth). +* Added `bin/preflight.sh` automated pre-deploy verification (PHPCS, PHPStan, PHPCompatibility, PHPUnit, composer audit, ZIP inspection). +* Added plugin header tests validating header consistency (`Stable tag` ↔ `Version` ↔ version constant). +* Added `.claude/settings.json` deny rules enforcing no-auto-deploy / no-push / no-tag / no-merge. +* Added `.claude/agents/security-auditor.md` and `.claude/commands/deploy-check.md` for clean-context pre-deploy audits. +* Fixed `bin/deploy.sh` excluding all `AGENTS-*.md` dev docs from the release ZIP (previously only `AGENTS.md`). -= 1.5.0 = += 1.8.1 = -* Added PicDefense as a third external scan provider: POST-based reverse image search with risk classification (high/medium/low), backlinks with similarity scores, risk flags (face/logo/landmark/stock/EXIF copyright), and label detection. -* Full Report page now shows PicDefense results with picRisk badge, risk flags list, backlinks table sorted by similarity score, and detected labels. -* Audit list modal shows picRisk badge inline next to the PicDefense match count. +* Network Operations buttons now use standard HTML POST Forms with server-side redirect (PRG pattern), replacing the AJAX approach that failed due to network admin script-loading constraints. +* Removed unreliable in-render script enqueueing for Network Tools page; operation buttons now work without JavaScript. +* JS dismiss/cancel labels on the Alerts page now use i18n strings from `wp_localize_script`. -= 1.4.0 = += 1.8.0 = -* Added full attachment detail page (Media Audit → Full Report) showing all raw external scan data: Google Vision pages/full/partial matches with clickable URLs; TinEye backlinks with page URL, image URL, and crawl date sorted newest-first. -* Fixed `rate_limit_per_minute` setting having no effect — providers were ignoring it and using a hardcoded value of 10. -* Fixed `external_batch_size` setting being ignored by Action Scheduler batch processing. -* Fixed TinEye domain extraction using the image CDN URL instead of the webpage URL (API field `backlink`, not `url`). - -= 1.2.0 = - -* Added browser-based AJAX scan runner in the Tools page (runs without WP-Cron or Action Scheduler). -* Added "Requeue scan errors" operation to retry failed external scans after fixing API key issues. -* Added "Last Scanned" column in the audit list, sortable with NULLs-last ordering. -* Added bulk action to export external scan results as a CSV file. - -= 1.1.0 = - -* Updated plugin URI, author, and contributor metadata. - -= 1.0.0 = - -* First stable release. Internal usage scanning, external reverse image search (Google Vision + TinEye), consensus detection, WP-CLI commands, GDPR privacy tools, and Spanish/Catalan translations. +* **WordPress Multisite support** — plugin now declares `Network: true` and can be activated network-wide, with two operating modes (per site / central) configurable from Network Admin → Media Audit → Settings. +* Network admin panel (central mode): aggregated Audit list, Alerts list, and Tools page showing all media across all sites, plus a network mirror table for fast cross-site queries. +* New site provisioning via `wp_initialize_site`; settings migration on first switch to central mode; network-aware uninstall. +* DB schema version bumped to `1.3.0`. = Previous versions = diff --git a/robotstxt-mediaaudit.php b/robotstxt-mediaaudit.php index 4e705f7..80b4aa1 100644 --- a/robotstxt-mediaaudit.php +++ b/robotstxt-mediaaudit.php @@ -3,9 +3,9 @@ * Plugin Name: Media Audit (by ROBOTSTXT) * Plugin URI: https://git.robotstxt.es/ROBOTSTXT/robotstxt-mediaaudit * Description: Internal media library usage auditing and external reverse image search to detect potential copyright issues. - * Version: 1.8.1 + * Version: 1.8.2 * Requires at least: 5.3 - * Tested up to: 7.0 + * Tested up to: 7.1 * Requires PHP: 8.0 * Requires Plugins: action-scheduler * Network: true @@ -24,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) { exit; } -define( 'ROBOTSTXT_MEDIAAUDIT_VERSION', '1.8.1' ); +define( 'ROBOTSTXT_MEDIAAUDIT_VERSION', '1.8.2' ); define( 'ROBOTSTXT_MEDIAAUDIT_DB_VERSION', '1.3.0' ); define( 'ROBOTSTXT_MEDIAAUDIT_PLUGIN_FILE', __FILE__ ); define( 'ROBOTSTXT_MEDIAAUDIT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); diff --git a/update.json b/update.json new file mode 100644 index 0000000..9fdf87b --- /dev/null +++ b/update.json @@ -0,0 +1,27 @@ +{ + "name": "Media Audit (by ROBOTSTXT)", + "slug": "robotstxt-mediaaudit", + "version": "1.8.2", + "download_url": "https://git.robotstxt.es/ROBOTSTXT/robotstxt-mediaaudit/releases/download/1.8.2/robotstxt-mediaaudit-1.8.2.zip", + "requires": "5.3", + "requires_php": "8.0", + "tested": "7.1", + "last_updated": "2026-08-08", + "author": "ROBOTSTXT", + "author_profile": "https://www.robotstxt.es/", + "homepage": "https://git.robotstxt.es/ROBOTSTXT/robotstxt-mediaaudit", + "description": "Audit your media library for copyright risks: track internal usage, run reverse image search via Google Vision, TinEye, and PicDefense, and classify results against configurable alert and ignored hostname lists.", + "changelog": "

1.8.2 - 2026-08-08

1.8.1 - 2026-06-08

1.8.0 - 2026-06-08

", + "sections": { + "description": "Audit your media library for copyright risks: track internal usage, run reverse image search via Google Vision, TinEye, and PicDefense, and classify results against configurable alert and ignored hostname lists.", + "changelog": "

1.8.2 - 2026-08-08

1.8.1 - 2026-06-08

1.8.0 - 2026-06-08

" + }, + "banners": { + "low": "", + "high": "" + }, + "icons": { + "1x": "", + "2x": "" + } +} From 7f89e98bbc3193bf64972fe38bc964de7178560c Mon Sep 17 00:00:00 2001 From: Javier Casares Date: Mon, 17 Aug 2026 18:51:45 +0000 Subject: [PATCH 2/3] v1.8.3 --- changelog.txt | 44 + includes/Admin/ManagerNotice.php | 209 ++++ includes/Admin/Settings.php | 2 + includes/Core/Plugin.php | 2 + includes/Network/Settings.php | 2 + includes/Network/ToolsPage.php | 11 +- languages/robotstxt-mediaaudit-ca.mo | Bin 24866 -> 32382 bytes languages/robotstxt-mediaaudit-ca.po | 1184 ++++++++++++----------- languages/robotstxt-mediaaudit-es_ES.mo | Bin 24674 -> 32065 bytes languages/robotstxt-mediaaudit-es_ES.po | 1183 +++++++++++----------- languages/robotstxt-mediaaudit.pot | 1019 ++++++++++++------- readme.txt | 23 +- robotstxt-mediaaudit.php | 11 +- robotstxt-updater.php | 383 -------- uninstall.php | 3 + update.json | 8 +- vendor/composer/autoload_classmap.php | 1 + vendor/composer/autoload_static.php | 1 + 18 files changed, 2230 insertions(+), 1856 deletions(-) create mode 100644 includes/Admin/ManagerNotice.php delete mode 100644 robotstxt-updater.php diff --git a/changelog.txt b/changelog.txt index d07cf8e..8e62ad3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,49 @@ == Changelog == += 1.8.3 = + +_Release date: 2026-08-17_ + +**Highlights** + +* Plugin updates are now delivered through [Manager (by ROBOTSTXT)](https://www.robotstxt.software/plugins/robotstxt-manager/) instead of a bundled self-updater. + +**Added** + +* Dismissible admin notice on the site and Network Admin Plugins pages when Manager (by ROBOTSTXT) is not installed and active, linking to `https://www.robotstxt.software/plugins/robotstxt-manager/` (dismissible per user; re-appears if the Manager is later removed). +* Permanent (non-dismissible) notice on the site and network Settings pages while Manager is not available. +* `Update URI` plugin header pointing to `https://www.robotstxt.software/plugins/robotstxt-mediaaudit/`. +* Header tests now also cover `Plugin URI`, `Update URI`, `Author`, `Author URI`, `Contributors`, contributor ordering, and absence of self-updater artifacts. + +**Fixed** + +* The dismissible Manager notice now also renders on the Network Admin plugins page (screen base `plugins-network`), not only on site-level Plugins. +* `uninstall.php` now always removes the per-user notice-dismissal user meta (UI state, not user data). + +**Changed** + +* Plugin URI and Author URI now point to `www.robotstxt.software`; `robotstxt` is listed first in the Contributors headers. +* `bin/preflight.sh` allowlist updated: `update.json` and `robotstxt-updater.php` are forbidden in the release ZIP. +* Dev tooling updated via `composer update` (PHPCS 3.13.6, WPCS 3.4.1, PHPStan 2.2.8, PHPUnit 9.6.36); PHPStan fix for transient cache typing in the Network Tools status reader. +* Translations completed for Spanish (es_ES) and Catalan (ca): all fuzzy and untranslated strings through 1.8.2 are now translated (326/326 in both locales). + +**Removed** + +* Bundled self-updater files `robotstxt-updater.php` and `update.json`. + +**Compatibility** + +* WordPress: 5.3 - 7.1 +* PHP: 8.0 - 8.5 +* WP-CLI: 2.x + +**Tests** + +* PHPCS (WordPress-Core, WordPress-Docs, WordPress-Extra): PASS +* PHPStan level 9: PASS +* PHPCompatibility 8.0-8.5: PASS +* PHPUnit: 11 tests, 54 assertions + = 1.8.2 = _Release date: 2026-08-08_ diff --git a/includes/Admin/ManagerNotice.php b/includes/Admin/ManagerNotice.php new file mode 100644 index 0000000..2be289d --- /dev/null +++ b/includes/Admin/ManagerNotice.php @@ -0,0 +1,209 @@ +base, array( 'plugins', 'plugins-network' ), true ) ) { + return; + } + + if ( self::is_manager_active() ) { + return; + } + + if ( ! current_user_can( 'install_plugins' ) ) { + return; + } + + if ( '1' === get_user_meta( get_current_user_id(), self::DISMISS_KEY, true ) ) { + return; + } + + $dismiss_url = wp_nonce_url( + add_query_arg( self::DISMISS_ARG, 1 ), + self::DISMISS_ACTION + ); + + echo '

' + . wp_kses( self::message(), array( 'a' => array( 'href' => true ) ) ) + . ' ' . esc_html__( 'Dismiss', 'robotstxt-mediaaudit' ) . '' + . '

'; + } + + /** + * Renders the permanent inline notice for the Settings pages (not dismissible). + * + * Used on both the site Settings page and the Network Settings page. + * + * @return void + */ + public static function render_settings_notice(): void { + if ( self::is_manager_active() ) { + return; + } + + echo '
' + . '

' . wp_kses( self::message(), array( 'a' => array( 'href' => true ) ) ) . '

' + . '
'; + } + + /** + * Handles the dismiss link: verifies the nonce, stores the dismissal (PRG). + * + * Also clears a stored dismissal once Manager is active again, so the + * notice returns if Manager is ever removed. + * + * @return void + */ + public static function handle_dismiss(): void { + $user_id = get_current_user_id(); + $dismissed = $user_id && get_user_meta( $user_id, self::DISMISS_KEY, true ); + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- presence check only; state changes require the nonce below. + if ( ! isset( $_GET[ self::DISMISS_ARG ] ) && ! $dismissed ) { + return; + } + + if ( self::is_manager_active() ) { + if ( $dismissed ) { + delete_user_meta( $user_id, self::DISMISS_KEY ); + } + return; + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no state change without the nonce check below. + if ( ! isset( $_GET[ self::DISMISS_ARG ] ) ) { + return; + } + + if ( ! current_user_can( 'install_plugins' ) ) { + return; + } + + check_admin_referer( self::DISMISS_ACTION ); + + update_user_meta( $user_id, self::DISMISS_KEY, '1' ); + + wp_safe_redirect( remove_query_arg( array( self::DISMISS_ARG, '_wpnonce' ) ) ); + exit; + } + + /** + * Builds the shared notice message (translation with a link placeholder). + * + * The result must be passed through wp_kses() at output time. + * + * @return string Unescaped message HTML. + */ + private static function message(): string { + return sprintf( + /* translators: %s: Manager plugin page URL. */ + __( 'To receive plugin updates, the plugin Manager (by ROBOTSTXT) must be installed and active.', 'robotstxt-mediaaudit' ), + esc_url( self::MANAGER_URL ) + ); + } +} diff --git a/includes/Admin/Settings.php b/includes/Admin/Settings.php index a395b77..230d9f0 100644 --- a/includes/Admin/Settings.php +++ b/includes/Admin/Settings.php @@ -963,6 +963,8 @@ class Settings {

+ +