{"id":180088,"date":"2024-02-26T10:05:37","date_gmt":"2024-02-26T10:05:37","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/blue-billywig\/"},"modified":"2026-08-03T08:20:13","modified_gmt":"2026-08-03T08:20:13","slug":"blue-billywig","status":"publish","type":"plugin","link":"https:\/\/dzo.wordpress.org\/plugins\/blue-billywig\/","author":21052357,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.0.4","stable_tag":"2.0.4","tested":"6.9.6","requires":"4.7","requires_php":"8.1.0","requires_plugins":null,"header_name":"Blue Billywig","header_author":"Blue Billywig","header_description":"A plugin for managing video content","assets_banners_color":"ccc6bc","last_updated":"2026-08-03 08:20:13","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/www.bluebillywig.com\/","rating":0,"author_block_rating":0,"active_installs":10,"downloads":3401,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"bluebillywigplugin","date":"2024-02-26 10:09:45"},"1.0.1":{"tag":"1.0.1","author":"bluebillywigplugin","date":"2024-04-18 11:15:40"},"1.0.10":{"tag":"1.0.10","author":"bluebillywigplugin","date":"2024-09-24 08:18:09"},"1.0.11":{"tag":"1.0.11","author":"bluebillywigplugin","date":"2024-11-11 15:52:25"},"1.0.12":{"tag":"1.0.12","author":"bluebillywigplugin","date":"2024-11-25 13:34:19"},"1.0.13":{"tag":"1.0.13","author":"bluebillywigplugin","date":"2024-12-30 09:16:10"},"1.0.14":{"tag":"1.0.14","author":"bluebillywigplugin","date":"2025-07-14 09:52:23"},"1.0.15":{"tag":"1.0.15","author":"bluebillywigplugin","date":"2025-07-14 09:55:59"},"1.0.2":{"tag":"1.0.2","author":"bluebillywigplugin","date":"2024-12-30 09:12:01"},"1.0.3":{"tag":"1.0.3","author":"bluebillywigplugin","date":"2024-05-02 08:58:07"},"1.0.4":{"tag":"1.0.4","author":"bluebillywigplugin","date":"2024-07-11 08:31:40"},"1.0.5":{"tag":"1.0.5","author":"bluebillywigplugin","date":"2024-07-22 07:57:56"},"1.0.6":{"tag":"1.0.6","author":"bluebillywigplugin","date":"2024-07-25 08:49:28"},"1.0.7":{"tag":"1.0.7","author":"bluebillywigplugin","date":"2024-08-05 07:19:09"},"1.0.8":{"tag":"1.0.8","author":"bluebillywigplugin","date":"2024-09-06 07:35:28"},"1.0.9":{"tag":"1.0.9","author":"bluebillywigplugin","date":"2024-09-12 08:21:52"},"2.0.1":{"tag":"2.0.1","author":"bluebillywigplugin","date":"2026-04-22 11:40:41"},"2.0.2":{"tag":"2.0.2","author":"bluebillywigplugin","date":"2026-04-24 09:15:46"},"2.0.3":{"tag":"2.0.3","author":"bluebillywigplugin","date":"2026-06-09 11:42:47"},"2.0.4":{"tag":"2.0.4","author":"bluebillywigplugin","date":"2026-08-03 08:20:13"}},"upgrade_notice":{"2.0.4":"<p>Bugfix release. Embeds placed in widget areas (sidebars) rendered as an invisible 0px placeholder because the frontend assets were only loaded when the current post&#039;s content contained a Blue Billywig block. Asset detection now covers widget areas, archive pages, and pasted video URLs, with a render-time safety net so an embed can never ship without its assets. No configuration changes; no breaking changes.<\/p>","2.0.3":"<p>Security and hardening release \u2014 recommended for all users. Fixes a stored-XSS vector in the JSON-LD SEO output: a clip title or description set in the Blue Billywig OVP could break out of the schema.org `` block and run JavaScript on every page embedding that clip. Also closes a capability gap that let Authors delete clips they don&#039;t own via the single-delete endpoint, and hardens several admin AJAX endpoints with input allowlisting, a missing nonce check, URL-path encoding, and a bound on library fetch size. No configuration changes required; no breaking changes.<\/p>","2.0.2":"<p>Fixes the dashboard video analytics widget: it read SAPI responses at the wrong path and quietly rendered the &quot;no data&quot; empty state even on publications with real traffic. After upgrading, the widget now shows Views \/ Player Loads \/ Play Rate, Top 5 Videos, and the device breakdown. Also adds a one-click admin notice when the plugin is installed but has no API credentials yet, and a red error panel inside the widget when a SAPI call fails (instead of the misleading empty state).<\/p>","2.0.1":"<p>This is the first 2.x release shipped via wordpress.org (2.0.0 was distributed via GitHub only). Upgrading from 1.x is a MAJOR update with breaking changes: requires PHP 8.1+, replaces the upload flow with direct browser-to-storage uploads, removes the blue_billywig_progress_list filter, and REST endpoints now require authentication. Read the 2.0 migration guide in USER-GUIDE.md before updating in production. 2.0.1 itself adds a fail-closed fix to the content-gate recursion cap, observability for gate-error and JWT-pepper persistence failures, a Solr-fq validation parity fix on a library AJAX endpoint, and regression tests.<\/p>","2.0.0":"<p>Major update with breaking changes. Direct browser-to-storage uploads via Uppy (no more PHP upload limits), Gutenberg blocks with SEO, oEmbed support, content protection, video analytics dashboard, folder browsing, and more. Requires PHP 8.1+. The blue_billywig_progress_list filter has been removed \u2014 see release notes if you used it in custom code.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3041421,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3041423,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3041424,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3041420,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3041418,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":{"bluebillywig\/channel":{"name":"bluebillywig\/channel","title":"Blue Billywig Channel"},"bluebillywig\/shorts":{"name":"bluebillywig\/shorts","title":"Blue Billywig Shorts"},"bluebillywig\/embed":{"name":"bluebillywig\/embed","title":"Blue Billywig Clip"}},"tagged_versions":["1.0.0","1.0.1","1.0.10","1.0.11","1.0.12","1.0.13","1.0.14","1.0.15","1.0.2","1.0.3","1.0.4","1.0.5","1.0.6","1.0.7","1.0.8","1.0.9","2.0.1","2.0.2","2.0.3","2.0.4"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3041425,"resolution":"1","location":"assets","locale":"","width":3020,"height":1432}},"screenshots":[]},"plugin_section":[262246],"plugin_tags":[224718,224717,224716,222],"plugin_category":[50],"plugin_contributors":[224719],"plugin_business_model":[],"class_list":["post-180088","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-billywig","plugin_tags-blue-billywig","plugin_tags-bluebillywig","plugin_tags-video","plugin_category-media","plugin_contributors-bluebillywig","plugin_committers-bluebillywigplugin"],"banners":{"banner":"https:\/\/ps.w.org\/blue-billywig\/assets\/banner-772x250.png?rev=3041418","banner_2x":"https:\/\/ps.w.org\/blue-billywig\/assets\/banner-1544x500.png?rev=3041420","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/blue-billywig\/assets\/icon.svg?rev=3041424","icon":"https:\/\/ps.w.org\/blue-billywig\/assets\/icon.svg?rev=3041424","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/blue-billywig\/assets\/screenshot-1.png?rev=3041425","caption":""}],"raw_content":"<!--section=description-->\n<p>The Blue Billywig plugin enhances Blue Billywig's capabilities by integrating with WordPress. This plugin allows users to seamlessly upload media directly to their Blue Billywig publication.<\/p>\n\n<p><strong>Documentation:<\/strong> See USER-GUIDE.md in the plugin directory for a complete walkthrough of all features.<\/p>\n\n<h3>Third-Party Service Integration<\/h3>\n\n<p>This plugin relies on the Blue Billywig Online Video Platform to provide video uploading capabilities. By using this plugin, you acknowledge and agree to the following:<\/p>\n\n<ul>\n<li>The plugin sends data to <a href=\"https:\/\/bbvms.com\/\">bbvms.com<\/a> and <a href=\"https:\/\/www.bluebillywig.com\/\">bluebillywig.com<\/a> for video management tasks.<\/li>\n<li>Your usage of Blue Billywig is subject to their <a href=\"https:\/\/www.bluebillywig.com\/privacy-statement\/\">Privacy Policy<\/a>.<\/li>\n<\/ul>\n\n<h3>Privacy &amp; GDPR<\/h3>\n\n<p><strong>What data is transmitted to Blue Billywig from site visitors?<\/strong><\/p>\n\n<p>Every page that embeds a Blue Billywig video loads the player from <code>{publication}.bbvms.com<\/code>. When the script runs, the visitor's IP address, User-Agent, Referer and page URL are transmitted to Blue Billywig so the player can fetch the clip, apply geo\/language rules, and record analytics.<\/p>\n\n<p><strong>Consent integration (GDPR \/ ePrivacy \/ TTDSG).<\/strong><\/p>\n\n<p>The plugin exposes a WordPress filter <code>bluebillywig_consent_granted<\/code> so consent-management plugins (Complianz, Cookiebot, Iubenda, Osano, \u2026) can defer loading the player until the visitor opts in:<\/p>\n\n<pre><code>add_filter( 'bluebillywig_consent_granted', function() { return your_cmp_has_video_consent(); } );\n<\/code><\/pre>\n\n<p>When this filter returns <code>false<\/code>, BB embed blocks, channel blocks, Shorts blocks, shortcodes, and oEmbed all render a neutral placeholder explaining that loading will transmit data to bbvms.com. No IP\/UA is shipped to Blue Billywig until consent is granted (or until the visitor reloads after accepting).<\/p>\n\n<p><strong>Cookies and local storage.<\/strong><\/p>\n\n<ul>\n<li>The plugin itself does not set any cookies or write to local storage from admin pages.<\/li>\n<li>On the frontend, the CTA email-capture overlay (when enabled by the editor) writes <code>bbtl_{clipId}<\/code> to the visitor's <code>localStorage<\/code> after a successful submit, so the form does not re-ask an engaged visitor. This key only carries the submitted email and a timestamp.<\/li>\n<li>The Blue Billywig player itself may set first-party cookies on the <code>.bbvms.com<\/code> domain for playback\/analytics. See Blue Billywig's privacy statement for details.<\/li>\n<\/ul>\n\n<p><strong>CTA email capture \u2014 consent.<\/strong><\/p>\n\n<p>The CTA editor can optionally include an email-capture form. The plugin requires an explicit consent checkbox on the form and rejects non-<code>https<\/code> submit endpoints. Captured emails are sent to the customer-configured endpoint (if any); Blue Billywig's own backend does not receive or store these emails.<\/p>\n\n<p><strong>Content protection JWT.<\/strong><\/p>\n\n<p>When you enable JWT-based content protection and a logged-in WordPress user requests a protected clip, a short-lived token (\u2264 7200s, clamped) is minted. The user-ID claim in the token is an HMAC of the WordPress user ID (never the raw ID) using <code>wp_salt('auth')<\/code>. Pages carrying a JWT emit <code>Cache-Control: private, no-store<\/code> to keep shared caches from serving one user's token to another.<\/p>\n\n<p><strong>Admin-side third-party code.<\/strong><\/p>\n\n<p>On plugin admin pages, the Uppy uploader library is bundled locally (<code>admin\/assets\/vendor\/uppy\/<\/code>) \u2014 no request to <code>releases.transloadit.com<\/code> on every admin page load.<\/p>\n\n<p><strong>Uninstall.<\/strong><\/p>\n\n<p>Uninstalling the plugin deletes every <code>blue-billywig-*<\/code> option (including stored API keys and the content-protection shared secret) and every <code>bb_*<\/code> transient. A reinstall starts from a clean slate.<\/p>\n\n<h3>Translators<\/h3>\n\n<p>The plugin ships with starter translations for German, French, Swedish, Danish, Norwegian Bokm\u00e5l, Finnish, and Dutch (in <code>\/languages\/<\/code>). They were generated automatically and cover every string, but they have not been reviewed by native speakers.<\/p>\n\n<p><strong>Please help us improve them.<\/strong> Once the plugin is listed on wordpress.org, any translation you contribute at <a href=\"https:\/\/translate.wordpress.org\/projects\/wp-plugins\/blue-billywig\/\">translate.wordpress.org\/projects\/wp-plugins\/blue-billywig<\/a> automatically rolls out to every site running that locale \u2014 no plugin release required. If you already translate other WordPress plugins, the process is the same; if you don't, it takes about five minutes to sign up and suggest a correction.<\/p>\n\n<p><strong>Adding a new language.<\/strong><\/p>\n\n<p>Once the plugin is listed on translate.wordpress.org, that is the preferred home for every language \u2014 translators suggest a locale and the site creates the project on first approval. If you want to kick-start a language ahead of that: copy <code>languages\/blue-billywig.pot<\/code> to <code>languages\/blue-billywig-&lt;locale&gt;.po<\/code> (e.g. <code>blue-billywig-it_IT.po<\/code> for Italian), translate the <code>msgstr<\/code> values, run <code>msgfmt blue-billywig-&lt;locale&gt;.po -o blue-billywig-&lt;locale&gt;.mo<\/code>, and send the files to <a href=\"https:\/\/support.bluebillywig.com\/\">support.bluebillywig.com<\/a>. We'll bundle them in the next release so translate.wordpress.org starts with a baseline.<\/p>\n\n<!--section=changelog-->\n<h4>2.0.4<\/h4>\n\n<ul>\n<li>Fix: Embed blocks placed in widget areas (sidebars) rendered an invisible 0px lazy placeholder \u2014 <code>bb-lazy.css<\/code>\/<code>js<\/code> were only enqueued when the current post's content contained a BB block, and widgets don't live in any post. Detection now covers all main-query posts (so archive pages work beyond the first post), active block widgets, classic Text widgets, and pasted bbvms.com oEmbed URLs; additionally every render path (blocks, shortcode, oEmbed) enqueues the assets at render time as a safety net, so embed markup can never ship without its assets \u2014 including FSE templates and reusable blocks the head-time scan can't see. (#19819)<\/li>\n<li>Fix: The CTA form overlay is part of the same two layers, so CTA forms now work on embeds in widget areas, archive pages, and render-time-only surfaces (previously singular pages with the embed in post content only).<\/li>\n<li>Fix: Pasted-URL detection matches exactly the URL shapes the oEmbed handlers embed (shared single source of truth), standing alone on their own line or in their own paragraph \u2014 matching what WordPress actually auto-embeds \u2014 so pages that merely link to a clip or show a URL in a code sample no longer load the embed assets.<\/li>\n<li>Fix: CTA text fields no longer swap on reload. Headline, body and button label are stored as one blob with empty fields stripped, so reading it back was a positional guess \u2014 a CTA with only a headline reloaded as a button label, and re-saving kept the swap. A marker class now records which fields were filled, so every combination round-trips correctly; older CTAs keep their existing reading. Image CTAs with a headline\/body but no button label also used to discard that text at save time, and now store it. (#19577)<\/li>\n<li>Fix: Corrupted <code>sidebars_widgets<\/code>\/<code>widget_block<\/code> option data degrades to \"no detection\" instead of fataling on the frontend; per-page-sidebar plugins are honoured via core's <code>sidebars_widgets<\/code> filter.<\/li>\n<li>Dev: New <code>bluebillywig_disable_lazy_loader<\/code> filter fully suppresses the bb-lazy assets on every layer (for themes shipping their own placeholder loader). <code>bluebillywig_enqueue_lazy_loader<\/code> is head-time-only as before 2.0.4 \u2014 sites that used <code>__return_false<\/code> on it to suppress all assets should switch to the new filter.<\/li>\n<li>Tests: <code>FrontendAssetDetectionTest<\/code> covers the detection matrix and behaviourally verifies each render path enqueues its assets (including before the consent gate); <code>has_shortcode<\/code> stub now mirrors core's registered-tag requirement.<\/li>\n<li>Dev: <code>.wp-env.json<\/code> + <code>package.json<\/code> are now committed so the manual browser-verification environment (<code>npx wp-env start<\/code>) is reproducible; see CLAUDE.md.<\/li>\n<\/ul>\n\n<h4>2.0.3<\/h4>\n\n<ul>\n<li>Security (Critical): JSON-LD output in <code>render_block()<\/code> is now encoded with <code>JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT<\/code>. Previously the schema.org payload was encoded with only <code>JSON_UNESCAPED_SLASHES<\/code>, so a clip whose title or description contained <code>&lt;\/script&gt;<\/code> (settable by anyone with OVP write access) broke out of the <code>&lt;script type=\"application\/ld+json\"&gt;<\/code> block and executed arbitrary JavaScript on every WordPress page embedding that clip (<code>iframe<\/code>, <code>oembed<\/code>, <code>url<\/code> and <code>amp<\/code> embed types). The encoder now also fails safe \u2014 emitting nothing and logging \u2014 when a payload can't be encoded, rather than shipping an empty script block. (#19070)<\/li>\n<li>Security (High): <code>delete_library_videos<\/code> (single-clip delete) now requires <code>delete_others_posts<\/code> via <code>require_destructive_cap()<\/code>, matching <code>bulk_action('delete')<\/code>. Previously it only required <code>publish_posts<\/code>, so an Author could delete clips they don't own through the single-delete endpoint while being blocked from bulk-delete. (#19071)<\/li>\n<li>Hardening: <code>update_clip_metadata<\/code> now validates <code>status<\/code> and <code>cpp<\/code> against an allowlist, and the same <code>status<\/code> allowlist is enforced on <code>init_upload<\/code>. (#19072)<\/li>\n<li>Hardening: <code>getEmbedCode<\/code> now <code>rawurlencode<\/code>s the playout and embed-type segments before interpolating them into the SAPI URL path, closing a path-injection surface. (#19073)<\/li>\n<li>Hardening: Per-video playout format is validated against the allowed set on save. (#19074)<\/li>\n<li>Hardening: Settings-form fields are validated against their allowed values before being persisted. (#19075)<\/li>\n<li>Hardening: <code>get_post_modal_template<\/code> now verifies a nonce before responding. (#19076)<\/li>\n<li>Hardening: Video IDs in <code>delete_library_videos<\/code> are coerced with <code>absint()<\/code>. (#19077)<\/li>\n<li>Performance\/Hardening: <code>load_library_videos<\/code> no longer issues an unbounded SAPI request; the result set is capped. (#19078)<\/li>\n<li>Tests: New <code>JsonLdEscapingTest<\/code>, <code>AjaxCapabilityTest<\/code> and <code>UpdateClipMetadataAllowlistTest<\/code> lock the above invariants. Full suite: 51 tests, 161 assertions.<\/li>\n<\/ul>\n\n<h4>2.0.2<\/h4>\n\n<ul>\n<li>Fix: Dashboard video analytics widget was reading SAPI responses at the wrong path (<code>['count']<\/code> \/ <code>['facets']<\/code> instead of <code>['body']['total']<\/code> \/ <code>['body']['facets']<\/code>, and facet entries use <code>'value'<\/code>, not <code>'val'<\/code>). <code>$total_views<\/code> and <code>$total_inits<\/code> always stayed 0, so the widget always rendered its \"No video views recorded\" empty state \u2014 even on publications with plenty of traffic. After upgrade the widget shows real data.<\/li>\n<li>UX: New <code>admin_notices<\/code> banner warns administrators when the plugin is installed but has no API credentials yet, with a one-click link to the settings page. Gated on <code>manage_options<\/code>, suppressed on the settings page itself, auto-clears as soon as credentials are saved.<\/li>\n<li>UX: When a SAPI analytics call fails the widget now renders a red error panel naming the failed sections and writes a structured <code>error_log<\/code> entry, instead of silently falling back to the empty-state copy. Makes \"API outage\" visually distinct from \"publication has no traffic\".<\/li>\n<li>Observability: On cache skip (at least one of the four analytics calls errored) the plugin now writes a single summary <code>error_log<\/code> line listing which section(s) failed, so transient SAPI outages surface in <code>debug.log<\/code> without waiting for a dashboard render.<\/li>\n<li>Performance: Dashboard widget transient TTL reduced from 15 to 5 minutes for slightly fresher stats; cache still busts immediately on credential changes via <code>SapiClient::clearCache()<\/code>, and errored responses are still never cached.<\/li>\n<li>Tests: +34 assertions in <code>AnalyzeWidgetDataTest<\/code> locking the SAPI envelope contract against the exact response-shape drift that caused the 2.0.1 regression.<\/li>\n<\/ul>\n\n<h4>2.0.1<\/h4>\n\n<ul>\n<li>Security: ContentGate::injectJwt now fails closed at the depth cap (previously returned un-gated content, so a crafted nested-base64 payload could bypass the gate).<\/li>\n<li>Security: Library AJAX endpoint now validates <code>bb_folder<\/code> and <code>bb-sourcetype<\/code> against the same regex the REST <code>\/search_media_clips<\/code> route uses (closes a Solr-fq injection surface the REST path had already closed).<\/li>\n<li>Hardening: <code>bulk_action<\/code> now verifies the CSRF nonce before the capability check (defence in depth).<\/li>\n<li>Observability: Content gate logs each distinct failure reason (SAPI disconnected \/ SAPI error \/ 404 \/ missing shared secret) and records a rolling one-hour tally. A new admin-notices banner surfaces the tally so operators can tell \"outage\" from \"misconfig\" instead of all paths rendering the same viewer message.<\/li>\n<li>Observability: JWT pepper persistence failure now triggers a persistent <code>notice-error<\/code> admin banner (<code>manage_options<\/code>-gated); previously every gated video silently failed verification with no signal.<\/li>\n<li>UX: Library AJAX endpoints (<code>load_library_videos<\/code>, <code>load_library_playlists<\/code>) surface the SAPI <code>Error<\/code> message via <code>wp_send_json_error<\/code> 502 instead of the prior generic 200 + \"No data\". Auth failures are now distinguishable from legitimately-empty libraries.<\/li>\n<li>Compat: Replaced <code>array_filter(..., 'strlen')<\/code> in <code>CtaTranslator<\/code> with a closure (PHP 8.1 deprecation, removed in 9).<\/li>\n<li>Docs: Corrected inaccurate comments in <code>CtaTranslator<\/code> (widget types) and subtitle upload (single-POST behaviour).<\/li>\n<li>Tests: +10 assertions via <code>ContentGateFailsClosedTest<\/code>, <code>UninstallAllowlistTest<\/code>, <code>SapiClientInvalidateClipCppCacheTest<\/code>; updated <code>ContentGateInjectJwtTest::test_depth_cap_fails_closed<\/code> to lock the fail-closed invariant.<\/li>\n<\/ul>\n\n<h4>2.0.0<\/h4>\n\n<ul>\n<li>New: Direct browser-to-storage upload via Uppy (no PHP upload limits)<\/li>\n<li>New: Gutenberg blocks with server-side rendering and JSON-LD SEO<\/li>\n<li>New: Channel embed Gutenberg block with channel selector<\/li>\n<li>New: oEmbed \u2014 paste BB video URLs directly to embed<\/li>\n<li>New: Video XML sitemap integration<\/li>\n<li>New: AI transcript display below videos (SEO-indexed)<\/li>\n<li>New: Video chapters with clickable timestamps (opt-in)<\/li>\n<li>New: Interactive video overlay support<\/li>\n<li>New: Folder tree browsing in library and media picker<\/li>\n<li>New: Video analytics dashboard widget<\/li>\n<li>New: Inline metadata editing in Media Library<\/li>\n<li>New: JWT-based content protection via WordPress roles<\/li>\n<li>New: Test Connection button on settings page<\/li>\n<li>Security: All AJAX handlers verify capabilities + nonces<\/li>\n<li>Security: Output escaping throughout, query injection fixes<\/li>\n<li>Performance: SAPI client singleton with response caching<\/li>\n<li>Compat: Requires PHP 8.1+, tested up to WordPress 6.9<\/li>\n<li>Breaking: blue_billywig_progress_list filter removed; use SapiClient class<\/li>\n<\/ul>\n\n<h4>1.0.15<\/h4>\n\n<ul>\n<li>Previous release.<\/li>\n<\/ul>","raw_excerpt":"The Blue Billywig plugin enhances Blue Billywig&#039;s capabilities by integrating with WordPress. This plugin allows users to seamlessly upload media &hellip;","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/180088","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=180088"}],"author":[{"embeddable":true,"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/bluebillywigplugin"}],"wp:attachment":[{"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=180088"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=180088"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=180088"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=180088"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=180088"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/dzo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=180088"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}