# Changelog — JPKCom ACF References

> Version history of the JPKCom ACF References WordPress plugin.

Source: https://www.jpkc.com/db/en/changelog/jpkcom-acf-references/

Version history of [`jpkcom-acf-references`](https://github.com/JPKCom/jpkcom-acf-references). Entries are taken verbatim from the project's README. A hands-on guide with tips is available in the [project guide](https://www.jpkc.com/db/en/guides/jpkcom-acf-references/).

## 1.2.1

* Fixed: two 1.2.0 release notes described `includes/references-data.php` as the single home of the visibility rule — one of them saying the abilities apply the shortcode's rule "not a copy of it". That is not what shipped. `jpkcom_acf_references_build_reference_query_args()` is called from `includes/abilities.php` and from nowhere else, while `includes/shortcodes.php` still assembles the same clauses inline. The two agree, and that was verified by comparing the generated SQL and the returned post IDs — but agreeing is not sharing, and two implementations can drift apart the moment either is edited. The claim mattered because it was the reason to trust that the abilities cannot disagree with the site's own listing; that trust now rests on a measurement rather than on a shared function. Both notes have been corrected and marked, and the developer documentation now records that a change to the rule has to be made in both files until the shortcode is moved over. No behaviour is affected

## 1.2.0
* Added: three read-only WordPress Abilities — `jpkcom-acf-references/list-filters`, `jpkcom-acf-references/query-references` and `jpkcom-acf-references/get-reference` — so AI assistants, MCP clients and REST automation can read your references as structured data instead of scraping the page. They are on by default for logged-in users with the `read` capability and can be switched off entirely with `define( 'JPKCOM_ACFREFERENCES_ABILITIES', false )`
* Added: the abilities return only references your site's own listing shows, and they apply the same visibility rule the `[jpkcom_acf_references_list]` shortcode applies. The short description and the image gallery are returned **only** for a reference whose page a visitor could actually open: one that redirects to an external URL and one whose expiry date has passed both have no public detail page, so those fields are withheld and the reason is stated in the answer. **This entry as originally published added "not a copy of it" about the shared rule. That was wrong — there are two implementations; see 1.2.1**
* Added: `includes/references-data.php`, which holds the visibility rule and the projection of a reference into plain data for the abilities. The shortcode returns exactly what it returned previously — verified by comparing the generated SQL and the returned post IDs before and after the change. **This entry as originally published read as though the rule had moved out of the shortcode. It did not; see 1.2.1**
* Added: `jpkcom_acf_references_ability_meta` and `jpkcom_acf_references_ability_capability` filters, so a site can change which abilities are exposed and who may run them
* Changed: WordPress 7.0 is now the minimum. Up to 6.9 an unexpected error inside an ability callback ended the whole request with a blank page instead of a readable message. From 7.0 WordPress catches it itself
* Note: filtering by a value that does not exist on this site returns **no** references and names the value under `unknown`, rather than quietly returning everything. A filter that is partly recognisable still narrows by the part that was

## 1.1.3
* Changed: the update manifest generator now defaults a missing `Network:` header to false instead of true, matching WordPress' own default. No change for this plugin, which declares `Network: true` explicitly
* CI: the lint and guard workflow now also runs on pushes to `main`. It only covered pull requests, so a direct push with bypass rights skipped every check
* Changed: comments, workflow step names and CI output across the repository are now English throughout, and the developer notes in `CLAUDE.md` were translated and trimmed. No effect on the shipped plugin

## 1.1.2
* Changed: `Tested up to` raised to WordPress 7.1
* Changed: the bundled updater's runtime floor now matches the plugin's own minimum. It bailed out below WordPress 6.8 while the plugin header has required 6.9 for several releases, so the check could never fire on a supported installation
* Docs: the remaining "WordPress 6.8" requirement statements now say 6.9, matching the plugin header
* CI: the release manifest's fallback values for `requires` and `tested` now say 6.9 and 7.1. They only apply when the README metadata cannot be read, but a stale fallback would have published a minimum the plugin no longer supports

## 1.1.1
* Changed: the plugin banners (`assets/banner-1544x500.avif`, `assets/banner-772x250.avif`) are now a plain `#3c4955` surface with no lettering

## 1.1.0
* Changed: the three taxonomy filters of the list shortcode use `tax_query` instead of `meta_query` with `LIKE` over the serialised ACF values. A leading wildcard cannot use an index, so each clause scanned every meta row for that key; the term relations ACF already writes are indexed. Verified against real data: identical results in all 14 filter combinations tested
* Fixed: on a multilingual site (WPML) the filter returned nothing in every secondary language. A shortcode carries the default-language term ID, the old clause searched for it in translations whose meta holds the translated ID, and found nothing. Measured: 0 results before, 6 after
* Fixed: `tools/check-term-sync.php` could not run at all. A `declare(strict_types=1)` sat halfway down the file, and `wp eval-file` evaluates the contents — so the documented invocation ended in a fatal error every time
* Fixed: the same script reported every translated post as drifted, because it compared raw meta against `wp_get_object_terms()`, which WPML rewrites to the current language
* Changed: non-numeric values in `type`, `filter_1` and `filter_2` now produce no filter rather than one that matches nothing

## 1.0.9
* **Fixed:** reference expiry was compared against the UTC date. WordPress sets the PHP timezone to UTC, so `date( 'Y-m-d' )` returns the UTC day and expired references stayed visible for the length of the site's UTC offset after local midnight (1–2 hours for Europe/Berlin). All three comparison sites now use `current_time( 'Y-m-d' )`
* **Fixed:** the single-reference redirect checked `current_user_can( 'administrator' )`, passing a role name where a capability belongs. That works only because the role is a key in the capability array, bypassing `map_meta_cap` and missing differently named roles with the same rights. Now checks `manage_options`, consistent with the sibling handlers in the same file
* **Added:** `tools/check-term-sync.php` — a read-only checker that reports whether the serialised ACF meta values and the real taxonomy term assignments agree. Required groundwork before the shortcode filters can move from unindexed `meta_query` + `LIKE` to indexed `tax_query`
* **Added:** `tests/test-conventions.php` — regression guards so neither the UTC date form nor a role-as-capability check can return unnoticed. Run in CI on every pull request

## 1.0.8
* Security: update packages are now verified *before* installation — the verified file is handed to WordPress instead of being downloaded a second time, so the bytes that were checked are the bytes that get installed
* Security: a missing or unfetchable SHA-256 checksum now aborts the update instead of installing unverified code (previously it silently skipped verification)
* Security: pinned every GitHub Action to a full commit SHA and added Dependabot with a 7-day cooldown, so a moved tag can no longer change the release build
* Security: tightened which download the updater claims, so sibling plugins cannot match each other's package
* Fixed: `sprintf()` calls in the updater bound named arguments to a variadic parameter, which raises `ArgumentCountError` on PHP 8.3
* Fixed: the "View Details" modal could fail with a `TypeError` when the manifest omitted `requires_plugins`
* Performance: a failed manifest fetch is now cached for an hour instead of being retried on every admin request
* Added: CI workflow on every pull request (PHP lint, named-argument check, YAML validation, action-pinning guard)
* Housekeeping: removed an unused `messages.mo` from the release package

## 1.0.7
* Fixed a leftover duplicate logo call in the reference customer partial that passed the ACF field array where an attachment ID is expected, which could render an unrelated image before every customer logo

## 1.0.6
* Raised the minimum WordPress version to 6.9 and "Tested up to" to WordPress 7.0
* Switched license metadata to the SPDX identifier `GPL-2.0-or-later` with the HTTPS license URI

## 1.0.5
* Security: updater prefers an exact match against the manifest `download_url` over the slug heuristic, so a tampered manifest can no longer bypass the checksum gate
* Security: timing-safe checksum comparison (`hash_equals()`) with an `is_string()` guard against `hash_file()` failures
* Security: manifest fetch via `wp_safe_remote_get()` (SSRF defense-in-depth)
* Fixed PHP warning and missing contributor names in the plugin detail popup (`display_name` now provided)
* Fixed PHP warning/deprecation on `wp plugin list` by completing the `no_update` transient entry (`new_version`, `package`, `tested`, `requires_php`)

## 1.0.4
* Fixed pagination layout not wrapping on small screens or with long post titles
* Pagination items now use `flex-wrap` and consistent `page-link` styling
* Previous/next links are hidden when no adjacent post exists
* Improved output escaping across all templates: `__()` → `esc_html__()`, `get_the_title()` → `esc_html()`, `get_field()` → `esc_html()`, `get_the_date()` → `esc_attr()`, `get_post_type_archive_link()` → `esc_url()`

## 1.0.3
* Fixed updater checksum verification failing on manual ZIP uploads (local file paths mistaken for URLs)
* Fixed release ZIP missing top-level directory, causing WordPress to not recognize plugin updates

## 1.0.2
* Added check for "SitePress" class

## 1.0.1
* Fix for incorrect database content caused by WPML

## 1.0.0
* Initial Release

