diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f0a2463 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,46 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +--- + +## [1.0.1] — 2026-05-17 + +### Security + +- **CSV MIME validation** — the importer now checks the uploaded file's MIME type server-side via `finfo` before opening it. The browser-side `accept=".csv"` attribute is not a security control and was the only prior gate. +- **Admin notice spoofing** — import result banners (success/error) are now delivered via a per-user transient rather than raw GET parameters. A crafted URL can no longer display false success or error messages to an admin. +- **HTML in translatable string** — the success notice now passes through `wp_kses()` restricted to ``, preventing a tampered translation file from injecting arbitrary markup. +- **`esc_url_raw` over `sanitize_url`** — switched to the canonical WordPress function for storing URLs (functionally equivalent; `sanitize_url` is an undocumented alias). +- **`sanitize_textarea_field` for CSV bio** — the importer previously used `wp_kses_post()`, which permitted HTML in a field described to users as plain-text. Now consistently plain-text. + +### Bug fixes + +- **`normalise_name()` null return** — `preg_replace()` returns `null` on failure (e.g. backtracking limit, invalid UTF-8). Both calls now fall back to the input string via `?? $name`, preventing a `TypeError` on PHP 8. +- **Revision saves** — the generic `save_post` hook was replaced with type-specific `save_post_tec_speaker` and `save_post_tribe_events` hooks. `save_post_tribe_events` does not fire for revisions (which have post type `'revision'`), so the separate `wp_is_post_revision()` guard is no longer needed. + +### Integration + +- **Correct SelectWoo handles** — The Events Calendar ships SelectWoo (a Select2 fork) under the handles `tribe-select2` (JS) and `tribe-select2-css` (CSS), not `select2`. The admin script loader now checks for `tribe-select2` first, then `select2`, then falls back to a CDN copy. Previously, TEC's registration was never detected and the CDN was always loaded unnecessarily. +- **Type-specific save hooks** — replaced the single generic `save_post` hook (priority 10) with `save_post_tec_speaker` and `save_post_tribe_events`. TEC hooks its own meta save to `save_post` at priority 15; using type-specific hooks eliminates any ordering dependency and makes intent explicit. + +### Improvements + +- **Activation hook** — added `register_activation_hook` to register the CPT and flush rewrite rules on activation, fixing the "Speaker archive returns 404 until Permalinks are re-saved" issue on first install. +- **`plugins_loaded` wrapper** — the plugin is now instantiated inside `add_action('plugins_loaded', …)` instead of at file-include time, ensuring all plugins (including TEC) are loaded before the constructor runs. +- **TEC dependency notice** — if The Events Calendar is not active, an admin notice is shown. TEC-specific hooks (event display, event meta box) are skipped; the Speaker CPT, admin menu, and shortcode remain functional. +- **Admin picker cap** — the event speaker picker query is capped at 200 results (`PICKER_LIMIT` constant) instead of an unbounded `posts_per_page => -1`, preventing memory issues on large installs. +- **`absint()` on option values** — speaker IDs written into `