# Blue Billywig WordPress Plugin - Release Notes

## Version 2.0.2

A bug-fix release that restores the Video Analytics dashboard widget shipped in 2.0.0 / 2.0.1. No breaking changes, no schema changes — safe to update in place.

### Fixed

- **Video Analytics dashboard widget now actually renders stats.** The widget read SAPI analytics responses at the wrong path: `['count']` / `['facets']` where the SDK hands back the decoded envelope `{type, body: {total, facets}}`, and facet entries use `'value'`, not `'val'`. `$total_views` and `$total_inits` therefore stayed at 0 on every render and the widget always fell into the "No video views recorded" empty-state branch, regardless of how much traffic the publication actually had. Reproduced and fixed live against a busy publication (widget was silent before, populated after).
- **Silent failure in the widget's empty-state branch.** A SAPI outage used to surface as the cheerful "No video views recorded in the last 7 days. Embed videos…" copy — indistinguishable from a publication with genuinely zero traffic. The renderer now tracks per-section errors explicitly and shows a red error panel naming the failed sections (plus a structured `error_log` line) when any SAPI call returns `WP_Error`. The empty-state copy now only appears when every call succeeded and both totals are zero.

### Added

- **Admin notice when the plugin is unconfigured.** A new `admin_notices` callback renders a dismissible warning on every wp-admin page when `SapiClient::isConnected()` is false, with a one-click link to the settings form. Gated on `manage_options`, suppressed on the settings page itself (it'd be noise there), and auto-disappears as soon as credentials are saved. Fills the gap where Upload / Library / Platform / Media-tab entry points used to go silent for first-run admins.
- **Structured `error_log` summary on cache skip.** When the widget's transient write is skipped because at least one SAPI call failed, the plugin now logs a single summary line listing which sections failed (`Blue Billywig dashboard widget: SAPI calls failed, skipping cache — inits: …; devices: …`) so transient outages surface in `debug.log` without waiting for a dashboard render.

### Changed

- **Widget transient TTL reduced from 15 to 5 minutes.** Slightly fresher stats without giving up the load-protection the cache exists for. Credential changes still bust the cache immediately via `SapiClient::clearCache()`, and errored responses are still never cached (the `has_errors` guard is unchanged).

### Tests

- New `AnalyzeWidgetDataTest` (6 tests, 34 assertions) locks the SAPI envelope contract: full-success fixture, old-broken-shape fallback (returns zeros, not crashes), single-section `WP_Error`, mixed partial failure, absent keys, and the specific `value` / `val` facet-key bug that shipped in 2.0.1. The logic was extracted into a static `Admin::analyze_widget_data( array $raw ): array` helper so the response-shape contract can be tested without standing up the full WP admin runtime.

### Migration notes

None. 2.0.2 is a drop-in patch release. Sites that already have credentials configured will start seeing actual widget data on the next dashboard render; sites without credentials will now see a yellow banner explaining what's missing.

## Version 2.0.1

A security and observability patch on top of the 2.0.0 rewrite. No breaking changes, no schema changes — safe to update in place.

### Fixed

- **Content gate fails closed at the recursion cap.** The `ContentGate::injectJwt` depth limit previously returned the original (un-gated) content when exceeded. A nested base64 payload engineered to overflow the cap could therefore bypass the gate entirely. It now returns the gate message and logs the anomaly.
- **Solr-fq injection gap on a library AJAX endpoint.** `load_library_videos` now validates the `bb_folder` and `bb-sourcetype` parameters against the same `^[A-Za-z0-9_\-]{1,N}$` regex the REST `/search_media_clips` route already used. The AJAX path had only `sanitize_text_field`, which the REST path had tightened in 2.0.0.
- **`bulk_action` verifies the CSRF nonce before checking capabilities** (defence in depth — don't branch on cap state before the CSRF check).

### Added — observability

- **Distinct gate-error logging + admin banner.** `filter_embed_block` and `filter_channel_block` now log each failure branch separately (SAPI disconnected / SAPI error / 404 / shared secret missing) and record a rolling one-hour tally in a transient. A new `admin_notices` hook renders a `notice-warning` banner summarising the tally so operators can tell "upstream outage" from "misconfiguration" instead of every path producing the same viewer message with no server-side signal.
- **JWT pepper persistence failure is now visible.** When `ContentGate::getJwtPepper()` cannot persist a pepper to `wp_options`, the plugin sets a transient and renders a `notice-error` admin banner (`manage_options`-gated). Previously this degraded state was invisible: every gated video silently failed verification.
- **Library AJAX endpoints surface SAPI errors.** `load_library_videos` and `load_library_playlists` now send `wp_send_json_error` 502 with the upstream `Error` message when SAPI returns one, instead of the prior generic 200 + `"error": "No data"`. Auth failures are now distinguishable from legitimately-empty libraries.

### Compatibility

- Replaced `array_filter(..., 'strlen')` in `CtaTranslator::combineText` / `splitText` with a closure. The `'strlen'` callback is deprecated in PHP 8.1 and scheduled for removal in PHP 9.

### Documentation

- Corrected the `CtaTranslator` class docblock (the class now emits image widgets and image+textLabel pairs, not only a single `textLabel`).
- Corrected the subtitle upload comment (`admin/includes/class-ajax.php`): SAPI's `/sapi/subtitle` POST persists `srt` directly; the previous "two-step POST then PUT" note was wrong.

### Tests

Full unit suite now runs 37 tests / 95 assertions (was 27 / 60). New test files:

- `ContentGateFailsClosedTest` — asserts that each error branch both renders the gate and records its reason in the tally transient; locks the cached-no-CPP pass-through so the happy path can't silently regress into always-gate.
- `UninstallAllowlistTest` — locks the uninstall invariants: every secret is wiped (API secret, CPP secret, JWT pepper), a sentinel third-party `blue-billywig-license` option sharing the `blue-billywig-` prefix survives, and every transient family from `SapiClient::clearCache` is targeted by a DELETE.
- `SapiClientInvalidateClipCppCacheTest` — the per-clip CPP cache drop is scoped to the target clip (siblings + channel caches untouched), and invalid input is a no-op rather than a wildcard nuke.
- `ContentGateInjectJwtTest::test_depth_cap_fails_closed` — replaces the prior fail-open assertion.

### Migration notes

None. 2.0.1 is a drop-in patch release. Content protection behaviour is unchanged for the happy path; the fix only changes what happens on error and on crafted input.

---

## Version 2.0.0

This is a major release that transforms the Blue Billywig plugin from a basic video embed tool into a full-featured video management solution for WordPress. Every aspect of the plugin has been improved: security, performance, features, and code quality.

### Breaking Changes

- **Upload flow completely replaced.** The old upload method (which sent video files through your WordPress server) has been removed. Uploads now go directly from the browser to Blue Billywig's storage using presigned URLs. This means no more PHP memory limits or upload timeouts, but any custom code that hooked into the old `blue_billywig_progress_list` filter will no longer work.
- **Minimum PHP version is 8.1.** The plugin now requires PHP 8.1 or higher.
- **REST API endpoints require authentication.** All REST endpoints now require the user to be logged in with appropriate capabilities. Unauthenticated API access is no longer possible.
- **The `blue_billywig_progress_list` filter has been removed.** If you used this filter in custom code, see the Migration Guide below.

### New Features

#### Uppy Upload with Presigned URLs
Video files are now uploaded directly from the browser to Blue Billywig's storage. Files never pass through your WordPress server, eliminating PHP memory limits, upload timeouts, and server disk space concerns. The upload interface uses Uppy with a Blue Billywig branded design, featuring drag-and-drop, progress tracking, and support for files up to 20 GB.

#### Gutenberg Blocks (Server-Side Rendered)
Two new Gutenberg blocks are available:

- **Blue Billywig Embed** - Embed a single video with search, playout selection, transcript display, and chapter navigation toggles.
- **Blue Billywig Channel** - Embed a full branded video channel portal with a dropdown selector for available channels.

Both blocks use server-side rendering for proper SEO. The embed block generates JSON-LD VideoObject schema markup for search engines.

#### oEmbed Support
Paste any Blue Billywig video or channel URL directly into the WordPress editor and it will automatically embed:
- `https://yourpub.bbvms.com/p/default/c/12345.html` - Video embed
- `https://yourpub.bbvms.com/view/default/12345.html` - Video embed
- `https://yourpub.bbvms.com/ch/199.html` - Channel embed

#### Folder Browsing
The video library now includes a folder tree sidebar that mirrors the folder structure from your Blue Billywig publication. Browse by folder in both the main Library page and the classic editor Media Library tab.

#### Video Analytics Dashboard Widget
A dashboard widget shows video performance at a glance:
- Total views and player loads (7-day window)
- Play rate percentage
- Top 5 most-viewed videos
- Device type breakdown

#### AI Transcript Display
When a video has an AI-generated transcript, it is displayed below the player in a collapsible section. This text is indexable by search engines, making every video findable by its spoken content.

#### Video Chapters
If a video has chapter markers, a clickable table of contents appears below the player. Chapters show timestamps and titles.

#### Interactive Video Support
Videos with interactive overlays (timelines) are detected automatically and rendered with appropriate container styles to support overlay elements.

#### Video XML Sitemap
Videos embedded via blocks or shortcodes are automatically included in WordPress's XML sitemap with title, description, thumbnail, duration, and player URL.

#### Inline Metadata Editing
Edit video title, description, tags, status, and content protection policy directly from the WordPress Media Library tab without leaving the admin.

#### Content Protection (JWT)
For publications using Content Protection Policies (CPP), the plugin can gate video access based on WordPress user roles. Authorized users receive a signed JWT token that is validated by the Blue Billywig platform. Unauthorized visitors see a configurable gate message with a login link.

#### Test Connection
The settings page now includes a "Test Connection" button that verifies your API credentials before saving.

### Security Fixes

- All AJAX handlers now verify user capabilities (`current_user_can`) in addition to nonce checks
- All HTML output is properly escaped with `esc_html()`, `esc_attr()`, and `esc_url()`
- REST API permission callbacks use `current_user_can('edit_posts')` instead of public access
- SAPI search queries escape special characters to prevent query injection
- Upload parts array is sanitized before forwarding to the API
- Content gate custom messages are sanitized with `wp_kses_post()`
- Session expiry returns proper JSON error responses instead of empty responses

### Bug Fixes

- Fixed unrecoverable crash when API credentials were invalid (now catches all exceptions)
- Fixed `ajaxurl` global mutation that caused progressive URL corruption
- Fixed playout dropdown building the wrong variable (`$inner_value` instead of `$tpl_playout`)
- Fixed modal strict comparison (`true === $_POST['modal']` never matched string POST data)
- Fixed `activate()` overwriting existing settings on plugin re-activation
- Fixed broken "Go to platform" link in the video modal
- Fixed block registration running only in admin context (blocks now render on the frontend)
- Fixed `WP_Error` class referenced without namespace prefix
- Fixed dashboard analytics widget caching error responses

### Performance

- Centralized SAPI client with singleton pattern eliminates redundant SDK initializations
- Embed codes are cached in WordPress transients (1 hour TTL)
- Publication data and playout lists are cached (1 hour TTL)
- Content Protection Policy list is cached (1 hour TTL)
- REST route registration consolidated into a single callback

### Code Quality

- Dead code, commented-out code, and debug statements removed
- Namespaces corrected (`BlueBillywigPlugin` instead of mixed `Admin` namespace)
- WordPress coding standards applied throughout
- All exceptions caught and converted to `WP_Error` with logging

---

## Migration Guide

### Upgrading from 1.0.x to 2.0.0

#### 1. Check PHP Version
Version 2.0.0 requires PHP 8.1 or higher. Check your PHP version in **Tools > Site Health > Info > Server**.

#### 2. Verify API Credentials
After updating, go to **Blue Billywig > Settings** and click **Test Connection** to verify your API credentials still work. If the plugin previously crashed on the settings page due to invalid credentials, this is now handled gracefully.

#### 3. Upload Changes
The upload page now uses Uppy with a drag-and-drop interface. Videos upload directly to Blue Billywig without passing through your WordPress server. No configuration changes are needed -- the new upload works with your existing API credentials.

If you had custom code using the `blue_billywig_progress_list` filter, that filter no longer exists. Use the `BlueBillywigPlugin\SapiClient` class directly instead.

#### 4. Existing Embeds
All existing shortcode embeds (`[blue-billywig-embed videoid="123"]`) continue to work without changes. Existing posts with the old Gutenberg block will continue to display, but you may want to re-save them to take advantage of server-side rendering and JSON-LD SEO markup.

#### 5. Content Protection (Optional)
If you use Content Protection Policies in Blue Billywig, you can now enable JWT-based content gating. There is no admin UI yet — configuration is done via WP-CLI, SQL, or a PHP snippet.

**See [USER-GUIDE.md → Content Protection → Setup walkthrough](USER-GUIDE.md#setup-walkthrough)** for the full step-by-step: creating a CPP in the OVP, copying its shared secret, saving it to WordPress, assigning the CPP to clips, and testing.

Quick reference:

```bash
# WP-CLI
wp option update blue-billywig-cp-enabled 1
wp option update blue-billywig-cp-secret 'YOUR_CPP_SHARED_SECRET'
wp option update blue-billywig-cp-roles '["administrator","editor","subscriber"]' --format=json
```

Replace `YOUR_CPP_SHARED_SECRET` with the secret you copied from the CPP Shared Secret rule in the Blue Billywig OVP.

#### 6. PHP Memory Limits No Longer Relevant
Because uploads no longer pass through WordPress, you can revert any PHP `upload_max_filesize` or `post_max_size` increases that were needed for the old upload method.
