{"id":294363,"date":"2026-05-11T07:53:30","date_gmt":"2026-05-11T07:53:30","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/spintax\/"},"modified":"2026-05-14T17:42:37","modified_gmt":"2026-05-14T17:42:37","slug":"spintax","status":"publish","type":"plugin","link":"https:\/\/su.wordpress.org\/plugins\/spintax\/","author":23448903,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.1.1","stable_tag":"2.1.1","tested":"6.9.4","requires":"6.2","requires_php":"8.0","requires_plugins":null,"header_name":"Spintax","header_author":"301st","header_description":"Template-based dynamic content generation using spintax markup. Create reusable templates with randomised text variants, variable substitution, and permutation logic.","assets_banners_color":"0c0f15","last_updated":"2026-05-14 17:42:37","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/spintax.net","header_author_uri":"https:\/\/301.st","rating":0,"author_block_rating":0,"active_installs":0,"downloads":168,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.5.0":{"tag":"1.5.0","author":"301st","date":"2026-05-11 07:53:09"},"2.0.0":{"tag":"2.0.0","author":"301st","date":"2026-05-12 15:54:30"},"2.0.1":{"tag":"2.0.1","author":"301st","date":"2026-05-12 19:04:28"},"2.0.2":{"tag":"2.0.2","author":"301st","date":"2026-05-12 19:29:39"},"2.0.3":{"tag":"2.0.3","author":"301st","date":"2026-05-12 22:18:14"},"2.1.0":{"tag":"2.1.0","author":"301st","date":"2026-05-14 11:55:34"},"2.1.1":{"tag":"2.1.1","author":"301st","date":"2026-05-14 17:42:37"}},"upgrade_notice":{"2.1.1":"<p>Bindings UX polish: Bulk Apply disables with a tooltip when Action Scheduler is missing, the stale-source banner promotes Run-now in that case, the ACF picker no longer empties out after a selection, and clean Run-now walks write a Logs entry so the success notice&#039;s CTA always has something to show.<\/p>","2.1.0":"<p>Admin UX overhaul. New Logs page closes the &quot;check logs&quot; gap. Bindings form is now three keyboard-friendly tabs with a real ACF combobox. TTL fields use presets. Stale banner + trigger warning + Run-now sync button on the list. No data migration; recommended for binding users.<\/p>","2.0.3":"<p>Adds runtime ACF target validation (closes a wrong-field-write path under ACF reactivation \/ WP-CLI imports), cumulative-failure tracking across Bulk Apply chunks (Stale badge no longer clears on partial failures), and a per-binding walk lock that refuses concurrent walks. Strongly recommended.<\/p>","2.0.2":"<p>Documentation refresh for the 2.0 binding surface (Action Scheduler as a recommended optional dependency, full WP-CLI command set, variable scopes, scheduling, manual edits) plus an admin notice on the Bindings page when Action Scheduler isn&#039;t loaded. No functional changes to the engine.<\/p>","2.0.1":"<p>Hot-fix for 2.0.0: cross-kind binding collisions, missing ACF field_key validation, Test panel scope-filter parity, Bulk Apply Stale-badge gating, and form value preservation on validation errors. Highly recommended if you&#039;re on 2.0.0.<\/p>","2.0.0":"<p>Major release \u2014 adds ACF \/ post-meta bindings, per-binding cron, Bulk Apply with Action Scheduler, full WP-CLI surface, and a one-shot migration wizard for <code>nested-spintax-for-acf<\/code> users. No breaking changes to the existing template \/ shortcode \/ render API.<\/p>","1.4.0":"<p>New <code>{?VAR?then|else}<\/code> conditional syntax, smarter sentence-end capitalisation around abbreviations, and a fix for <code>#set<\/code> directives with empty values.<\/p>","1.1.0":"<p>Per-element permutation separators, auto-spacing for word separators, improved input sanitization.<\/p>","1.0.1":"<p>Fixes permutation config handling, preview rendering, and scope isolation. Recommended update.<\/p>","1.0.0":"<p>Initial release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3528366,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3528366,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3528366,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3528366,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3528366,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.5.0","2.0.0","2.0.1","2.0.2","2.0.3","2.1.0","2.1.1"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3528366,"resolution":"1","location":"assets","locale":"","width":1280,"height":800},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3528366,"resolution":"2","location":"assets","locale":"","width":1280,"height":800},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3528366,"resolution":"3","location":"assets","locale":"","width":1280,"height":800},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3532366,"resolution":"4","location":"assets","locale":"","width":1280,"height":800}},"screenshots":{"1":"Template editor with spintax markup and live preview.","2":"Settings page with global variables editor.","3":"Template list with shortcode, cache status, and cron schedule.","4":"Binding edit form: three-tab layout (Source &amp; Target \/ Behavior \/ Test), ACF combobox with stable field-key autofill, post-type and status scope filters, shared-template vs per-post source modes."}},"plugin_section":[],"plugin_tags":[8494,2487,186,47760,4516],"plugin_category":[43,55],"plugin_contributors":[255945],"plugin_business_model":[],"class_list":["post-294363","plugin","type-plugin","status-publish","hentry","plugin_tags-content-generation","plugin_tags-dynamic-content","plugin_tags-seo","plugin_tags-spintax","plugin_tags-templates","plugin_category-customization","plugin_category-seo-and-marketing","plugin_contributors-301st","plugin_committers-301st"],"banners":{"banner":"https:\/\/ps.w.org\/spintax\/assets\/banner-772x250.png?rev=3528366","banner_2x":"https:\/\/ps.w.org\/spintax\/assets\/banner-1544x500.png?rev=3528366","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/spintax\/assets\/icon.svg?rev=3528366","icon":"https:\/\/ps.w.org\/spintax\/assets\/icon.svg?rev=3528366","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/spintax\/assets\/screenshot-1.png?rev=3528366","caption":"Template editor with spintax markup and live preview."},{"src":"https:\/\/ps.w.org\/spintax\/assets\/screenshot-2.png?rev=3528366","caption":"Settings page with global variables editor."},{"src":"https:\/\/ps.w.org\/spintax\/assets\/screenshot-3.png?rev=3528366","caption":"Template list with shortcode, cache status, and cron schedule."},{"src":"https:\/\/ps.w.org\/spintax\/assets\/screenshot-4.png?rev=3532366","caption":"Binding edit form: three-tab layout (Source &amp; Target \/ Behavior \/ Test), ACF combobox with stable field-key autofill, post-type and status scope filters, shared-template vs per-post source modes."}],"raw_content":"<!--section=description-->\n<p>Spintax is a content-generation toolkit for WordPress. Author reusable templates in spintax markup (enumerations, permutations, conditionals, plural agreement), then either embed them inline via shortcodes \/ PHP, or <strong>bind them to ACF and post-meta fields so every matching post gets its own rendered variant on save, on a cron schedule, or on demand<\/strong>. A built-in Logs page surfaces what each Bulk Apply \/ Run-now walk did; a WP-CLI surface covers staging-to-production sync.<\/p>\n\n<p><strong>Key features:<\/strong><\/p>\n\n<ul>\n<li><strong>Enumerations<\/strong> <code>{a|b|c}<\/code> \u2014 randomly pick one option, with nesting support<\/li>\n<li><strong>Permutations<\/strong> <code>[&lt;config&gt;a|b|c]<\/code> \u2014 pick N elements, shuffle, join with custom separators<\/li>\n<li><strong>Variables<\/strong> <code>%var%<\/code> \u2014 global, local (<code>#set<\/code>), and shortcode-level variable scopes<\/li>\n<li><strong>Conditionals<\/strong> <code>{?VAR?then|else}<\/code> \u2014 render a branch based on whether a variable is set (also <code>{?!VAR?then}<\/code> inverted)<\/li>\n<li><strong>Plural agreement<\/strong> <code>{plural &lt;count&gt;: form1|form2|form3}<\/code> \u2014 pick grammatically correct noun form by count. RU\/UK\/BE 3-form (one|few|many), EN-style 2-form (one|many). First spintax engine with first-class plurals.<\/li>\n<li><strong>Nested templates<\/strong> \u2014 embed templates within templates via <code>#include<\/code> or <code>[spintax]<\/code><\/li>\n<li><strong>ACF \/ post-meta bindings (NEW in 2.0)<\/strong> \u2014 configure once per post type, render Spintax templates into ACF text\/textarea\/wysiwyg fields or post-meta keys on every matching post. Auto-seed empty fields, preserve manual edits, Bulk Apply via Action Scheduler.<\/li>\n<li><strong>Object cache<\/strong> \u2014 rendered output cached via WP Object Cache API (Redis\/Memcached ready)<\/li>\n<li><strong>Cron regeneration<\/strong> \u2014 optional scheduled cache refresh per template, plus per-binding cron walks<\/li>\n<li><strong>WP-CLI<\/strong> \u2014 <code>wp spintax bindings list|apply|test|export|import<\/code><\/li>\n<li><strong>Validation<\/strong> \u2014 bracket matching, circular reference detection, syntax checking<\/li>\n<li><strong>Admin UI<\/strong> \u2014 code editor, live preview, shortcode copy, settings page, bindings list<\/li>\n<\/ul>\n\n<p><strong>Syntax based on the GTW (Generating The Web) standard.<\/strong><\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin does <strong>not<\/strong> connect to any external services, APIs, or third-party servers.<\/p>\n\n<p>All content generation happens locally on your WordPress server. No data is sent externally. No remote requests are made during activation, rendering, or caching.<\/p>\n\n<h3>Privacy Policy<\/h3>\n\n<p>This plugin does not collect, store, or transmit any personal user data. It does not use cookies, tracking pixels, analytics, or any form of telemetry.<\/p>\n\n<p>Templates and their rendered output are stored entirely within your WordPress database and object cache.<\/p>\n\n<h3>Credits<\/h3>\n\n<ul>\n<li>Syntax based on the <a href=\"https:\/\/spintax.net\">GTW (Generating The Web)<\/a> standard<\/li>\n<li>Developed by <a href=\"https:\/\/301.st\">301st<\/a><\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>spintax<\/code> folder to <code>\/wp-content\/plugins\/<\/code><\/li>\n<li>Activate the plugin through the 'Plugins' menu in WordPress<\/li>\n<li>Create templates under the \"Spintax\" menu in the admin sidebar<\/li>\n<li>Embed templates using <code>[spintax slug=\"my-template\"]<\/code> in posts\/pages or <code>spintax_render('my-template')<\/code> in theme files<\/li>\n<\/ol>\n\n<p><strong>Recommended optional dependency:<\/strong> install <a href=\"https:\/\/wordpress.org\/plugins\/action-scheduler\/\">Action Scheduler<\/a> if you plan to use the \"Bulk Apply\" button on ACF \/ post-meta bindings, or schedule bindings via per-binding cron on a site with many matching posts. The plugin works without it \u2014 admins can use the synchronous \"Run now\" button on each binding card, and the same walk is available as <code>wp spintax bindings apply --binding=&lt;id&gt; --all<\/code>. Action Scheduler turns those into one-click chunked async jobs that don't block the request. If you already use WooCommerce or another plugin that bundles Action Scheduler, you're already set; the Bindings page only shows the install notice when AS isn't loaded.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"how%20do%20i%20create%20a%20template%3F\"><h3>How do I create a template?<\/h3><\/dt>\n<dd><p>Go to Spintax &gt; Add New in the WordPress admin. Enter a title and your spintax markup in the editor.<\/p><\/dd>\n<dt id=\"what%20syntax%20does%20the%20plugin%20use%3F\"><h3>What syntax does the plugin use?<\/h3><\/dt>\n<dd><ul>\n<li><code>{a|b|c}<\/code> \u2014 randomly picks one option<\/li>\n<li><code>[a|b|c]<\/code> \u2014 permutation: picks N elements, shuffles, joins with space<\/li>\n<li><code>[&lt;minsize=2;maxsize=3;sep=\", \";lastsep=\" and \"&gt; a|b|c|d]<\/code> \u2014 configured permutation<\/li>\n<li><code>%variable%<\/code> \u2014 variable reference<\/li>\n<li><code>#set %var% = value<\/code> \u2014 local variable definition<\/li>\n<li><code>{?VAR?then|else}<\/code> \u2014 conditional: render a branch by truthiness of <code>%VAR%<\/code> (also <code>{?!VAR?then}<\/code> inverted)<\/li>\n<li><code>{plural %Count%: form1|form2|form3}<\/code> \u2014 plural agreement: picks the correct grammatical form by count (RU 3-form, EN 2-form)<\/li>\n<li><code>\/#comment#\/<\/code> \u2014 block comment (stripped from output)<\/li>\n<li><code>#include \"slug\"<\/code> \u2014 embed another template<\/li>\n<\/ul>\n\n<p>Full syntax reference with examples and a live playground: https:\/\/spintax.net\/docs\/syntax<\/p><\/dd>\n<dt id=\"where%20can%20i%20learn%20more%3F\"><h3>Where can I learn more?<\/h3><\/dt>\n<dd><ul>\n<li><strong>Documentation hub:<\/strong> https:\/\/spintax.net\/docs\/ \u2014 guides, reference, recipes<\/li>\n<li><strong>Compact syntax reference:<\/strong> https:\/\/spintax.net\/docs\/syntax \u2014 all primitives in one page (13 languages)<\/li>\n<li><strong>Plural agreement guide:<\/strong> https:\/\/spintax.net\/docs\/plural-spintax\/ \u2014 <code>{plural N: form1|form2|form3}<\/code> in depth (EN\/RU)<\/li>\n<li><strong>Conditional spintax guide:<\/strong> https:\/\/spintax.net\/docs\/conditional-spintax\/ \u2014 <code>{?VAR?then|else}<\/code> value-driven branching (EN\/RU)<\/li>\n<li><strong>Authoring mindset:<\/strong> https:\/\/spintax.net\/docs\/authoring-mindset\/ \u2014 write the final text first, add markup last (EN\/RU)<\/li>\n<li><strong>Live playground:<\/strong> https:\/\/spintax.net\/play\/ \u2014 write a template, set variables, render N variants in your browser (EN\/RU)<\/li>\n<\/ul><\/dd>\n<dt id=\"does%20caching%20require%20redis%20or%20memcached%3F\"><h3>Does caching require Redis or Memcached?<\/h3><\/dt>\n<dd><p>The plugin uses the WordPress Object Cache API. With a persistent backend (Redis, Memcached), cached output persists across requests. Without one, templates are re-rendered on each page load.<\/p><\/dd>\n<dt id=\"can%20i%20pass%20variables%20through%20shortcodes%3F\"><h3>Can I pass variables through shortcodes?<\/h3><\/dt>\n<dd><p>Yes: <code>[spintax slug=\"greeting\" name=\"Alice\" city=\"Moscow\"]<\/code> makes <code>%name%<\/code> and <code>%city%<\/code> available inside the template.<\/p><\/dd>\n<dt id=\"what%20are%20acf%20%2F%20post-meta%20bindings%3F\"><h3>What are ACF \/ post-meta bindings?<\/h3><\/dt>\n<dd><p>A binding pairs a Spintax template (or a per-post inline source) with one target field on one post type \u2014 for example \"Posts \u2192 ACF: hero_subtitle\". Configure it once under Spintax \u2192 Bindings and the plugin populates the field on every matching post on save, on a cron schedule, or on demand via Bulk Apply. Manual edits are preserved by default (hash-tracked); flags control whether the binding auto-seeds empty fields, regenerates on every save, or clears the field when the template renders to empty.<\/p><\/dd>\n<dt id=\"can%20i%20bind%20to%20acf%20fields%3F\"><h3>Can I bind to ACF fields?<\/h3><\/dt>\n<dd><p>Yes. Bindings support both ACF (text \/ textarea \/ wysiwyg, top-level fields) and plain post-meta keys. ACF Free and Pro are both supported; nested fields (repeater \/ flexible_content rows) are not supported in 2.0 \u2014 that lands in a later release. The form-side field picker auto-fills the stable ACF field key so writes work on the first save without ACF's reference-meta handshake.<\/p><\/dd>\n<dt id=\"do%20i%20need%20action%20scheduler%3F\"><h3>Do I need Action Scheduler?<\/h3><\/dt>\n<dd><p>It's a recommended optional dependency for binding-heavy sites. The plugin works without it: admins can run a walk via the synchronous <strong>Run now<\/strong> button on each binding card, or <code>wp spintax bindings apply --binding=&lt;id&gt; --all<\/code> from the CLI. What Action Scheduler adds is chunked async execution, so:<\/p>\n\n<ul>\n<li>The admin <strong>Bulk Apply<\/strong> button can dispatch a non-blocking background job instead of holding the request.<\/li>\n<li>Per-binding cron schedules enqueue an async job instead of running the walk inline on the cron tick \u2014 useful on large catalogues where the synchronous path risks PHP-FPM timeouts.<\/li>\n<\/ul>\n\n<p>Many WP shops already ship Action Scheduler bundled with WooCommerce or other plugins \u2014 check Plugins \u2192 Installed Plugins for \"Action Scheduler\" before installing it separately. If the Bindings admin page shows an \"Action Scheduler is not installed\" notice at the top, you don't have it loaded yet.<\/p><\/dd>\n<dt id=\"what%27s%20the%20difference%20between%20bulk%20apply%20and%20run%20now%3F\"><h3>What's the difference between Bulk Apply and Run now?<\/h3><\/dt>\n<dd><p>Both walk every matching post for a binding and produce the same writes. They differ in <em>how<\/em> the walk runs:<\/p>\n\n<ul>\n<li><strong>Bulk Apply<\/strong> \u2014 dispatches the walk to Action Scheduler as chunked async jobs. The request returns immediately and you can watch progress on the Logs page. Requires Action Scheduler.<\/li>\n<li><strong>Run now<\/strong> \u2014 runs the entire walk synchronously in the current request. No async dependency, but the page blocks until the walk finishes. Available to administrators, and the recommended path on sites without Action Scheduler.<\/li>\n<\/ul>\n\n<p>When Action Scheduler isn't loaded, the Bulk Apply button is disabled with a tooltip pointing at Run now \/ WP-CLI; the stale-source banner on the binding edit form promotes Run now to its primary action.<\/p><\/dd>\n<dt id=\"where%20do%20i%20see%20bulk%20apply%20or%20run%20now%20progress%3F\"><h3>Where do I see Bulk Apply or Run now progress?<\/h3><\/dt>\n<dd><p><strong>Spintax \u2192 Logs<\/strong> in the admin sidebar. Both paths log a completion entry per walk (e.g. <code>Bulk Apply run_synchronously completed for binding &lt;id&gt; \u2014 wrote=N skipped=M cleared=K.<\/code>), plus warnings for partial failures. The Logs page supports level filtering, substring search, and pagination; entries are kept in a ring buffer sized by Settings \u2192 Spintax \u2192 Max log entries.<\/p><\/dd>\n<dt id=\"what%20wp-cli%20commands%20does%20the%20plugin%20add%3F\"><h3>What WP-CLI commands does the plugin add?<\/h3><\/dt>\n<dd><p>Five subcommands under <code>wp spintax bindings<\/code>:<\/p>\n\n<ul>\n<li><code>wp spintax bindings list [--format=table|json|csv]<\/code> \u2014 list all bindings on the site.<\/li>\n<li><code>wp spintax bindings apply --binding=&lt;id&gt; [--all|--post=&lt;id&gt;]<\/code> \u2014 run a binding against every matching post (<code>--all<\/code>) or a single post (<code>--post=&lt;id&gt;<\/code>). This is the synchronous fallback path for Bulk Apply.<\/li>\n<li><code>wp spintax bindings test --binding=&lt;id&gt; --post=&lt;id&gt;<\/code> \u2014 dry-run a binding against one post and report what would be written (target value, rendered preview, skip reason). Same logic as the admin Test panel; use this instead of <code>apply<\/code> when you want a preview.<\/li>\n<li><code>wp spintax bindings export {--binding=&lt;id&gt;|--all} [&gt; bindings.json]<\/code> \u2014 emit one binding or the full store as JSON to stdout, deduped by <code>(post_type, target.key)<\/code>.<\/li>\n<li><code>wp spintax bindings import --file=bindings.json [--overwrite] [--dry-run]<\/code> \u2014 import bindings from JSON. <code>--overwrite<\/code> updates matches on the same target triple; without it, duplicates are skipped. Use <code>--dry-run<\/code> to preview the plan without writing.<\/li>\n<\/ul>\n\n<p>The export\/import pair is the recommended staging\u2192production sync path; bindings are not exposed over REST in 2.0.<\/p><\/dd>\n<dt id=\"what%20variables%20can%20i%20use%20inside%20a%20bound%20template%3F\"><h3>What variables can I use inside a bound template?<\/h3><\/dt>\n<dd><p>A binding template sees four layered variable sources (later layers override earlier ones):<\/p>\n\n<ul>\n<li><strong>Global variables<\/strong> \u2014 the <code>#set<\/code> block in Settings \u2192 Spintax. Site-wide.<\/li>\n<li><strong>Per-binding overrides<\/strong> \u2014 a <code>#set<\/code> block in the binding's Variables tab. Applies to that binding only.<\/li>\n<li><strong>Post context<\/strong> (opt-in checkbox) \u2014 <code>%post_id%<\/code>, <code>%post_title%<\/code>, <code>%post_url%<\/code>, <code>%post_slug%<\/code>, <code>%post_date%<\/code>, <code>%post_modified%<\/code>, <code>%author_id%<\/code>, <code>%author_name%<\/code>.<\/li>\n<li><strong>ACF sibling fields<\/strong> (opt-in checkbox, ACF-target bindings only) \u2014 every top-level text \/ textarea \/ wysiwyg field in the same ACF group, available as <code>%acf_&lt;field_name&gt;%<\/code>. Siblings are always fresh on save: the binding runs after ACF persists.<\/li>\n<\/ul>\n\n<p>The binding's source can also use the rest of the Spintax syntax (<code>{a|b|c}<\/code>, <code>[a|b]<\/code>, <code>{?VAR?then|else}<\/code>, <code>{plural %N%: \u2026}<\/code>, <code>#include \"slug\"<\/code>, <code>\/#comment#\/<\/code>).<\/p><\/dd>\n<dt id=\"how%20do%20i%20schedule%20bindings%20to%20run%20automatically%3F\"><h3>How do I schedule bindings to run automatically?<\/h3><\/dt>\n<dd><p>Two trigger paths, both configurable per binding under \"Triggers\":<\/p>\n\n<ul>\n<li><strong>Fire on post save<\/strong> (checkbox, default on) \u2014 runs after the post (and ACF, if present) finishes saving. Skipped on autosaves, bulk-edits, batch REST imports, revisions, and trash flips.<\/li>\n<li><strong>Cron schedule<\/strong> (dropdown: disabled \/ hourly \/ twicedaily \/ daily) \u2014 each binding gets its own scheduled tick. With Action Scheduler installed the tick enqueues an async walk; without it, the walk runs synchronously on the cron worker.<\/li>\n<\/ul>\n\n<p>For a one-off \"apply now\", click <strong>Bulk Apply<\/strong> (async, needs Action Scheduler) or <strong>Run now<\/strong> (synchronous, admins) on the binding card.<\/p><\/dd>\n<dt id=\"how%20does%20the%20plugin%20handle%20manual%20edits%20to%20bound%20fields%3F\"><h3>How does the plugin handle manual edits to bound fields?<\/h3><\/dt>\n<dd><p>Each binding signs its last-rendered value and re-checks the target before every write. With <strong>Preserve manual edits<\/strong> enabled (default):<\/p>\n\n<ul>\n<li>If the current value still matches the last render, the binding is free to regenerate.<\/li>\n<li>If the value has been edited outside the binding, the run is skipped and the skip is logged.<\/li>\n<\/ul>\n\n<p>Pair this with <strong>Regenerate on every save<\/strong> for a \"refresh on save unless edited\" workflow. With <strong>Auto-seed empty fields<\/strong> alone, the binding only writes when the target is empty \u2014 manual edits are preserved by definition.<\/p>\n\n<p>Cold-start safety net: when a binding first sees a post with non-empty target content and no prior render on file, it treats the existing value as a manual baseline and skips that post until the field is cleared or the binding's \"Initialize from current value\" flow is run.<\/p><\/dd>\n<dt id=\"i%20edited%20a%20template.%20why%20aren%27t%20the%20changes%20showing%20up%20on%20the%20front%20end%3F\"><h3>I edited a template. Why aren't the changes showing up on the front end?<\/h3><\/dt>\n<dd><p>Bindings are a <strong>pre-generation<\/strong> system, not a render-on-read layer. The rendered string is stored in the target field; consumers (themes, blocks, REST readers) get that stored value directly. Editing the source template doesn't propagate to existing posts until a trigger writes a fresh value to each one.<\/p>\n\n<p>When you edit a template that has bindings pointing at it, the plugin:<\/p>\n\n<ol>\n<li>Bumps an internal render-cache version on each affected binding.<\/li>\n<li>Surfaces an admin notice on the template-edit screen (\"N bindings depend on this template\").<\/li>\n<li>Shows a \"Stale: source template edited\" badge on each affected binding's card.<\/li>\n<\/ol>\n\n<p>To push the new content to existing posts, click <strong>Bulk Apply<\/strong> on each affected binding (or run <code>wp spintax bindings apply --binding=&lt;id&gt; --all<\/code> from the CLI). The Stale badge only clears when the entire walk completes with zero failures \u2014 partial-failure walks keep the badge so you notice the divergence and retry.<\/p><\/dd>\n<dt id=\"is%20there%20a%20hard%20cap%20on%20bindings%3F\"><h3>Is there a hard cap on bindings?<\/h3><\/dt>\n<dd><p>200 bindings per site. The store is a single autoloaded option (~500 bytes per binding), and the cap keeps autoload memory bounded. If you genuinely need more, please open an issue with your use case.<\/p><\/dd>\n<dt id=\"which%20fields%20can%27t%20i%20bind%20to%3F\"><h3>Which fields can't I bind to?<\/h3><\/dt>\n<dd><p>The form rejects a handful of unsafe targets at save time:<\/p>\n\n<ul>\n<li>WordPress-internal meta keys (anything starting with <code>_wp_<\/code>, <code>_edit_<\/code>, <code>_oembed_<\/code>, etc.).<\/li>\n<li>Plugin-internal <code>_spintax_*<\/code> slots used to store source, signatures, and cache versions.<\/li>\n<li><code>wp_posts<\/code> columns like <code>post_title<\/code>, <code>post_content<\/code>, <code>post_excerpt<\/code>. These are not post-meta and writing to them via the meta API would silently create shadow rows.<\/li>\n<li>The same target name already bound by another binding \u2014 one binding per (post type, target field), whether the kind is ACF or post-meta.<\/li>\n<li>For ACF targets: the stable ACF field key must be present and resolvable when ACF is loaded.<\/li>\n<\/ul><\/dd>\n<dt id=\"on%20multisite%2C%20are%20bindings%20shared%20across%20the%20network%3F\"><h3>On multisite, are bindings shared across the network?<\/h3><\/dt>\n<dd><p>No \u2014 bindings are per-site. Each subsite manages its own. Use <code>wp --url=site2 spintax bindings import --file=site1-bindings.json<\/code> to copy bindings between subsites via the WP-CLI export\/import round-trip.<\/p><\/dd>\n<dt id=\"can%20i%20manage%20bindings%20via%20rest%3F\"><h3>Can I manage bindings via REST?<\/h3><\/dt>\n<dd><p>Not in 2.0; bindings are admin-only. The <code>wp spintax bindings<\/code> WP-CLI surface covers staging\u2192production sync scenarios. REST API exposure is tracked for a later release.<\/p><\/dd>\n<dt id=\"i%27m%20coming%20from%20%60nested-spintax-for-acf%60.%20is%20there%20a%20migration%20path%3F\"><h3>I'm coming from `nested-spintax-for-acf`. Is there a migration path?<\/h3><\/dt>\n<dd><p>Yes. After activating Spintax 2.0, a dismissible admin banner points to <strong>Tools \u2192 Spintax Migration<\/strong>. The wizard scans for predecessor data, shows a per-row preview, and creates bindings deduped by <code>(post type, target field)<\/code>. Per-post sources and variables are copied non-destructively \u2014 the old plugin's data stays in place until you delete it.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>2.1.1<\/h4>\n\n<ul>\n<li>UX (Bindings list): \"Bulk Apply\" button now disables and exposes a tooltip pointing at the Run-now \/ WP-CLI fallback when Action Scheduler isn't installed \u2014 previously the click hit the <code>no_action_scheduler<\/code> error path so users had to click-to-learn.<\/li>\n<li>UX (Bindings list): clean synchronous Run-now walks now write a log entry too (<code>Bulk Apply run_synchronously completed for binding &lt;id&gt; \u2014 wrote=N skipped=M cleared=K<\/code>), so the \"View details in Logs \u2192\" CTA on the success notice always lands on a populated page. Previously only failures logged.<\/li>\n<li>UX (Bindings form): the stale-source banner above the edit form now mirrors the list-view's Bulk Apply \/ Run-now pair. Without Action Scheduler, Bulk Apply is disabled with the same explanatory tooltip and Run-now is promoted to the primary action \u2014 previously the banner's only CTA routed straight into the no-AS error path.<\/li>\n<li>UX (Bindings form): the ACF field picker no longer collapses to an empty list after you select a field and refocus the input. The display string the picker writes (<code>name (field_key)<\/code>) is now stripped before the haystack filter, so \"browse without retyping\" works as documented.<\/li>\n<li>UX (Bindings form): defensive \u2014 the ACF field-key row is hidden unless <code>kind=acf_field<\/code> exactly (previously hid only when <code>kind=post_meta<\/code>, leaving an empty-kind edge case where the row could render without <code>hidden<\/code>).<\/li>\n<li>UX (Bindings list): Run-now capability failure now redirects back to the binding's edit form, consistent with the rest of the binding-edit error paths, instead of bouncing to the silent list view.<\/li>\n<li>UX (Settings): the <strong>Max log entries<\/strong> ring-buffer size (clamped 10\u20135000, default 200) is now exposed as a form field on Settings \u2192 Spintax. The option key was already wired up internally; this release adds the missing control.<\/li>\n<li>Internal: tightened <code>test_run_now_handler_rejects_non_admin<\/code> \u2014 replaces a conditional soft assertion with two unconditional invariants (walk-lock never acquired, no \"Wrote N\" success flash).<\/li>\n<li>Internal: 520 PHPUnit tests, 950 assertions (was 514 \/ 938 in 2.1.0).<\/li>\n<\/ul>\n\n<h4>2.1.0<\/h4>\n\n<ul>\n<li>UX (Settings): Spintax Settings is now also reachable from the Spintax submenu (under Bindings), not only from WP Settings \u2192 Spintax \u2014 both menu paths resolve to the same page.<\/li>\n<li>UX (Settings): Default Cache TTL and per-template Cache TTL no longer use a bare seconds input. Both surfaces now offer human presets (No caching \/ 1 hour \/ 6 hours \/ 1 day \/ 1 week \/ 1 month) plus a \"Custom\u2026\" option for any exact-seconds value.<\/li>\n<li>UX (Settings): \"Purge All Template Caches\" button moved inline into the Default Cache TTL row.<\/li>\n<li>UX (Bindings): New <strong>Logs<\/strong> admin page under Spintax \u2192 Logs \u2014 newest-first table of ring-buffer entries with level filter, substring search, pagination clamped to settings.logs_max. Editors view; admins clear. Replaces the pre-2.1.0 \"Check logs\" admin notice that pointed at a screen that didn't exist.<\/li>\n<li>UX (Bindings): Admin notices that point at logs (Bulk Apply enqueued, etc.) now ship a real \"View progress in Logs \u2192\" link. The flash-notice trait accepts a <code>{text, action_url, action_label}<\/code> payload alongside legacy strings.<\/li>\n<li>UX (Bindings): Binding edit \/ create form is now a three-tab layout (Source &amp; Target \/ Behavior \/ Test). WAI-ARIA tablist + keyboard navigation (Arrow keys, Home \/ End). Validation errors redirect back to whichever tab the offending field lives on; the active tab survives the PRG round-trip via flash transient and <code>?active_tab=<\/code> query arg.<\/li>\n<li>UX (Bindings): ACF field picker on the form is now a custom searchable combobox (replaces the buggy <code>&lt;input list&gt;<\/code> + <code>&lt;datalist&gt;<\/code>). Group \u2192 field grouping, substring search across group \/ label \/ name, full ARIA combobox semantics, and clicking a row autofills both the field name and the stable ACF field key in one go.<\/li>\n<li>UX (Bindings): Inline \"This binding will never run\" warning under Triggers when both <code>save_post<\/code> is off and cron is disabled \u2014 live update on checkbox \/ select change so editors notice the problem before submitting.<\/li>\n<li>UX (Bindings): Stale-source banner above the binding form (and an inline \"Bulk Apply now\" button) when the persisted binding's source template has been edited since the last walk.<\/li>\n<li>UX (Bindings): \"Run now\" button next to Bulk Apply (admins only, gated on debug=true OR Action Scheduler absent). Runs the walk synchronously via <code>BulkApply::run_synchronously()<\/code> \u2014 useful for dev sites without cron traffic and for installs without Action Scheduler. Walk-status badge (\"Running (started Ns ago)\") appears on the card while the per-binding walk lock is held.<\/li>\n<li>UX (Bindings): The \"Action Scheduler is not installed\" notice is now per-user dismissible via a new <code>wp_ajax_spintax_dismiss_admin_notice<\/code> endpoint. Whitelisted notice ids prevent the endpoint from filling <code>wp_usermeta<\/code> with arbitrary rows.<\/li>\n<li>UX (Bindings): Stale \"Phase 3 will add a dropdown\" copy removed (Phase 3 shipped in 2.0.0). \"Bind to a Spintax template (DRY across posts)\" softened to \"Shared template \u2014 render the same source on every matching post\".<\/li>\n<li>Internal: AdminNotice trait extended with backward-compat for both legacy <code>{message, type}<\/code> and the new rich payload. BindingsPage constructor now accepts an optional <code>BulkApply<\/code> for test injection. New shared <code>Spintax\\Support\\TtlField<\/code> helper backs the preset \/ custom TTL widget. Notice action-url is <code>esc_url()<\/code>-filtered so <code>javascript:<\/code> and other unsafe schemes are stripped.<\/li>\n<li>Internal: 73 new PHPUnit cases \u2014 TTL preset \/ custom resolver, Settings + meta-box save paths, dual-menu registration, AdminNotice payload shapes, LogsPage filtering \/ pagination \/ capability gating, Bindings tabs ARIA + PRG round-trip, ACF combobox rendering, dismissible notice endpoint, stale banner from persisted entity (not flash draft), trigger warning visibility, Run now handler gates + walk badge thresholds. 514 tests total (was 441).<\/li>\n<\/ul>\n\n<h4>2.0.3<\/h4>\n\n<ul>\n<li>Fix: ACF target validation now runs on every apply, not just at form save. <code>BindingApplier::plan()<\/code> rejects bindings whose stored <code>target.field_key<\/code> no longer resolves to a field with the expected name (deleted, renamed, or re-assigned in ACF). Two new return codes: <code>skip_acf_not_loaded<\/code> (ACF deactivated since the binding was saved) and <code>skip_invalid_acf_field<\/code> (key + name disagreement). Closes a path where CLI-imported or imported-while-ACF-inactive bindings could write through <code>update_field()<\/code> to the wrong field.<\/li>\n<li>Fix: <code>BindingApplier::read_target()<\/code> and <code>::write_target()<\/code> no longer fall back to plain <code>update_post_meta()<\/code> \/ <code>get_post_meta()<\/code> for <code>kind = acf_field<\/code> when ACF isn't loaded. The applier short-circuits at the runtime guard above, so the low-level methods are the sole writer for verified targets. Pre-2.0.3 the silent fallback could write the rendered value to a post-meta row ACF would never see again.<\/li>\n<li>Fix: Bulk Apply now tracks failures cumulatively across chunks via a persistent <code>_spintax_binding_walk_failed_v_&lt;id&gt;<\/code> flag. The final chunk gates <code>stamp_last_applied_version()<\/code> on the cumulative flag. 2.0.1 only checked the current chunk, so a multi-chunk walk that failed in chunk 1 and succeeded in the final chunk would still clear the Stale badge.<\/li>\n<li>Fix: Concurrent Bulk Apply walks on the same binding are now refused with <code>WP_Error 'walk_in_progress'<\/code>. Both <code>enqueue()<\/code> and <code>run_synchronously()<\/code> acquire a per-binding lock (option <code>_spintax_binding_walk_lock_&lt;id&gt;<\/code>) at walk start; stale locks older than one hour are auto-overwritten so a crashed walk doesn't permanently jam the binding.<\/li>\n<li>Internal: 11 new PHPUnit cases \u2014 runtime ACF guard, multi-chunk failure tracking, walk-lock acquisition \/ release, stale-lock recovery. 441 tests total (was 430).<\/li>\n<li>Tooling: <code>npm run lint:php<\/code> and <code>lint:php:fix<\/code> moved to <code>scripts\/lint-php.sh<\/code> \/ <code>scripts\/lint-php-fix.sh<\/code>. The inline command tripped over bash-c quoting on Windows. <code>.gitattributes<\/code> enforces LF endings on shipped text files.<\/li>\n<li>Internal: CLI <code>wp spintax bindings import --overwrite<\/code> help text updated to reflect the 2.0.1 <code>(post_type, target.key)<\/code> uniqueness contract.<\/li>\n<\/ul>\n\n<h4>2.0.2<\/h4>\n\n<ul>\n<li>Docs: new FAQ entries \u2014 Action Scheduler dependency, full <code>wp spintax bindings<\/code> WP-CLI surface, variable scopes (global \/ per-binding \/ post context \/ ACF siblings), trigger options (save_post + per-binding cron), manual edit detection, template-edit propagation, reserved-key tiers.<\/li>\n<li>Docs: Installation section now flags Action Scheduler as a recommended optional dependency with the specific features it enables.<\/li>\n<li>UX: Spintax \u2192 Bindings shows an info notice at the top of the page when Action Scheduler isn't loaded, explaining the two features that degrade (admin Bulk Apply, async cron walks) and linking to the install screen. Notice disappears when AS is loaded by any source (direct install, WooCommerce \/ Jetpack bundle, mu-plugin, etc.).<\/li>\n<li>Internal: no functional changes to the bindings engine or core spintax engine \u2014 patch is documentation + a single admin-page notice.<\/li>\n<\/ul>\n\n<h4>2.0.1<\/h4>\n\n<ul>\n<li>Fix: ACF and post-meta bindings on the same <code>(post_type, field name)<\/code> no longer coexist \u2014 they wrote to the same database row and silently raced. Tier 4 uniqueness now ignores <code>target.kind<\/code>. Existing pre-2.0.1 conflicts remain in the data store but the next save of either binding will reject.<\/li>\n<li>Fix: ACF bindings now require a non-empty <code>target.field_key<\/code> and validate it against the live ACF field when ACF is loaded. Previously a missing or mistyped field key could route <code>update_field()<\/code> writes to a different field.<\/li>\n<li>Fix: Test panel and Bulk Apply now report <code>skip_out_of_scope_type<\/code> \/ <code>skip_out_of_scope_status<\/code> for posts that wouldn't match the binding's scope in live triggers. Two new applier return codes \u2014 total now 11 instead of 9.<\/li>\n<li>Fix: Bulk Apply only clears the Stale badge when the walk had zero failures. Partial-failure walks keep the binding flagged so editors notice the divergence and retry.<\/li>\n<li>Fix: Binding form validation errors no longer throw the editor back to the list view \u2014 the form re-renders with submitted values via a short-lived transient flash, with the specific error inline.<\/li>\n<li>Internal: 21 new PHPUnit cases covering each fix path; bindings unit suite is now exhaustive on scope-filter, cross-kind dedup, ACF field_key validation, and Bulk Apply stamp gating.<\/li>\n<\/ul>\n\n<h4>2.0.0<\/h4>\n\n<ul>\n<li><strong>ACF \/ post-meta bindings<\/strong> \u2014 a Spintax template (or a per-post inline source) can now be bound to any ACF text\/textarea\/wysiwyg field or post-meta key on a post type. Configure once under Spintax \u2192 Bindings and the plugin populates the field on save, cron, or via Bulk Apply.<\/li>\n<li>Decision-tree write behaviour with four flags: <code>auto_seed_empty<\/code> (default on; never clobbers existing content), <code>regenerate_on_save<\/code>, <code>preserve_manual_edits<\/code> (hash-tracks the last rendered value so external edits are detected), <code>clear_on_empty<\/code>. Cold-start behaviour documented to avoid false manual-edit positives.<\/li>\n<li>Per-binding cron schedules (hourly \/ twicedaily \/ daily) registered as individual <code>wp_schedule_event<\/code> hooks per binding.<\/li>\n<li>Bulk Apply via Action Scheduler with chunked processing; a clean WP-CLI fallback when Action Scheduler isn't installed.<\/li>\n<li>New <code>%post_id%<\/code>, <code>%post_title%<\/code>, <code>%post_url%<\/code>, <code>%post_slug%<\/code>, <code>%post_date%<\/code>, <code>%post_modified%<\/code>, <code>%author_id%<\/code>, <code>%author_name%<\/code> post-context variables \u2014 opt-in per binding.<\/li>\n<li>New <code>%acf_&lt;field_name&gt;%<\/code> variables \u2014 opt-in per binding, exposes ACF sibling fields in the same group.<\/li>\n<li>Template-edit cascade \u2014 editing a Spintax template that is referenced by bindings bumps an internal cache version and surfaces a notice telling the editor that stored target fields will refresh on the next Bulk Apply \/ cron \/ save_post.<\/li>\n<li><code>wp spintax bindings list|apply|test|export|import<\/code> \u2014 full WP-CLI surface for staging\u2192production workflows and Action-Scheduler-less environments.<\/li>\n<li>One-shot migration helper at <strong>Tools \u2192 Spintax Migration<\/strong> for users coming from the predecessor plugin <code>nested-spintax-for-acf<\/code>. Detects, previews, and imports legacy data deduped by <code>(post_type, target.key)<\/code>. Original predecessor data is never deleted by the migration.<\/li>\n<li>Reserved-key guard rejects WP-internal meta keys, plugin-internal <code>_spintax_*<\/code> prefixes, wp_posts column names, and duplicate <code>(post_type, target.kind, target.key)<\/code> triples at form save.<\/li>\n<li>Hard cap of 200 bindings per site (single autoloaded option size budget).<\/li>\n<li>Per-binding chunk size override in the Advanced form section.<\/li>\n<li>Uninstall cleans every bindings option family and sibling post-meta \u2014 no orphan rows left behind.<\/li>\n<li>Internal: 398+ PHPUnit tests, including exhaustive decision-tree coverage and migration import edge cases.<\/li>\n<\/ul>\n\n<h4>1.5.0<\/h4>\n\n<ul>\n<li>Add: plural agreement primitive <code>{plural &lt;count&gt;: form1|form2|form3}<\/code> \u2014 pick the correct grammatical form by count. RU\/UK\/BE = 3 forms (<code>one|few|many<\/code>); EN\/ES\/PT\/DE etc. = 2 forms (<code>one|many<\/code>). Count is a <code>%var%<\/code> reference or literal integer (resolved after variable expansion, so helper-var patterns via <code>#set<\/code> work). Locale comes from per-template post meta <code>_spintax_locale<\/code> or the WordPress site locale. Lenient at runtime: malformed constructs render verbatim with fullwidth braces instead of crashing the page. First spintax engine to treat plural as a first-class primitive.<\/li>\n<li>Add: validator surface for plural blocks \u2014 structural check (form slot rejects nested <code>{}<\/code>, <code>[]<\/code>) always on; arity check (RU expects 3, EN expects 2) when locale is known.<\/li>\n<li>Internal: 74 PHPUnit cases mirroring the canonical TS implementation (<code>spintax-plurals.test.ts<\/code> in casino-platform). Engine classes <code>Plurals<\/code>, <code>PluralArityError<\/code>, <code>PluralFormError<\/code> ship alongside <code>Conditionals<\/code> from 1.4.0.<\/li>\n<\/ul>\n\n<h4>1.4.0<\/h4>\n\n<ul>\n<li>Add: conditional syntax <code>{?VAR?then|else}<\/code> \u2014 render a branch based on whether a variable is set\/non-empty (also <code>{?!VAR?then}<\/code> for inverted, optional else). Resolves both before and after <code>%var%<\/code> expansion, so conditionals inside variable values work too.<\/li>\n<li>Add: single-token abbreviation whitelist in post-processing \u2014 known shorthands like <code>\u0441\u043e\u0446.<\/code>, <code>\u044d\u043b.<\/code>, <code>Mr.<\/code>, <code>Inc.<\/code> no longer trigger sentence-end capitalisation of the next word. Covers Russian editorial\/address\/unit shorthands plus English titles and business suffixes.<\/li>\n<li>Fix: <code>#set<\/code> directive with an empty value (<code>#set %x% =<\/code>) no longer silently swallows the next directive on the following line.<\/li>\n<li>Fix: HTML start tags inside permutation alternatives (e.g. <code>[&lt;li&gt;item&lt;\/li&gt;|&lt;li&gt;...]<\/code>) are no longer mis-parsed as a <code>&lt;config&gt;<\/code> block.<\/li>\n<li>Improve: cache description in template meta box and global settings now explains that visitors see the same generated variant per runtime context until expiry or regeneration.<\/li>\n<li>Internal: regression tests for IDN domains flanked by Cyrillic letters and for randomisation behaviour across renders.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Add: per-element permutation separators \u2014 assign custom separator to each element via <code>&lt; sep &gt;<\/code> before <code>|<\/code><\/li>\n<li>Add: auto-spacing for purely alphabetic word separators (e.g. <code>&lt;and&gt;<\/code>, <code>&lt;\u0438\u043b\u0438&gt;<\/code>)<\/li>\n<li>Security: sanitize raw spintax input with custom sanitize_spintax() \u2014 strips invalid UTF-8, null bytes, and control characters while preserving angle-bracket syntax<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Fix: permutation minsize\/maxsize logic when only one parameter is specified<\/li>\n<li>Fix: preview rendering no longer strips spintax config from template input<\/li>\n<li>Fix: child templates no longer inherit parent's local #set variables<\/li>\n<li>Improve: global variables editor now uses #set textarea (paste full blocks)<\/li>\n<li>Improve: validation errors displayed on template edit screen with line numbers<\/li>\n<li>Improve: \"Regenerate Public Cache\" now forces fresh subtree render<\/li>\n<li>Add: demo template created on first activation<\/li>\n<li>Add: SECURITY.md with responsible disclosure policy<\/li>\n<li>Add: Privacy Policy and External Services sections in readme.txt<\/li>\n<li>Code: PHPCS 0 errors, full WP.org review compliance<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<li>GTW-compatible spintax engine with nested enumerations and permutations<\/li>\n<li>Template CPT with code editor and admin preview<\/li>\n<li>Shortcode and PHP rendering API<\/li>\n<li>Object cache with versioned keys and cascade invalidation<\/li>\n<li>Per-template cron regeneration<\/li>\n<li>Global and local variable scopes<\/li>\n<li>Settings page with global variables editor<\/li>\n<\/ul>","raw_excerpt":"Spintax templates with ACF \/ post-meta bindings, Bulk Apply, Logs, and WP-CLI \u2014 generate dynamic content at scale on WordPress.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/294363","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=294363"}],"author":[{"embeddable":true,"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/301st"}],"wp:attachment":[{"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=294363"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=294363"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=294363"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=294363"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=294363"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/su.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=294363"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}