Skip to content

Releases: davidofchatham/bws-gb-dynamic-tags-extensions

v1.19.1

Choose a tag to compare

@github-actions github-actions released this 03 Sep 18:19

Fixed

  • Turning on "Show time at midnight" no longer drops AM/PM consolidation from a time range. A range whose two ends share a meridiem renders as 9:00–11:30 AM in both settings; previously enabling the option silently expanded it to 9:00 AM–11:30 AM. The option now governs midnight display only, as its label says.

  • An all-day range stored as 12:00 AM to 11:59 PM now renders as a date, with no stray end time. 11:59 PM on the END of a range is now read as an unset end time, mirroring how 12:00 AM is read as an unset start time, unless "Show time at midnight" is enabled. Both a same-day range (previously August 24, 11:59 PM) and a multi-day one (previously August 24 – August 26 11:59 PM) are affected.

  • A time range whose start and end time are the same now renders the time once, instead of repeating it as if it were a range.

  • {{image}}'s configured fallback now renders on archive, search, 404 and home-page contexts. It previously rendered nothing on those pages even with a Media Library fallback set, though the fallback still worked correctly on individual posts, terms and author archives.

  • {{try_image}}'s fallback now resolves the configured Media Library image, instead of printing the raw attachment ID as text. This affected every as mode (URL, alt text, ID, caption), including a nonexistent attachment ID, which previously printed too instead of correctly rendering empty.

  • {{content}}, {{datetime_single}} and {{datetime_range}}'s configured fallbacks now render on the same archive, search, 404 and home-page contexts where they previously rendered nothing, for the same underlying reason as the {{image}} fix above.

  • use:title now works on every context-modifier text tag (e.g. {{term_text use:title}}). It previously rendered empty; the base {{text}} tag was unaffected.

  • use:key now works on every context-modifier content tag (e.g. {{term_content use:key}}), and use:excerpt now works on custom post-entity-based _content tags. Both previously rendered the post content or term description instead of the field or excerpt — silently wrong, not empty. A term entity has no excerpt of its own, so use:excerpt there is unchanged: it still renders the term description.

  • The field key picker now shows immediately in a try_ tag's Attempt A controls. Previously, try_ tags which default to a meta/option field (e.g. Try Text and Image) needed the Field selector switched to another option, then back, before its key picker would appear.

Changed

  • Custom code calling bws_format_single_date_time() or bws_format_date_range() directly must rename the smart_time key in the options array it passes to hide_midnight. The value and its meaning are unchanged, and there is no compatibility alias. The old name had come to describe two unrelated behaviors; only midnight suppression remains. This does not affect tags placed in the editor, tag strings, or the showMidnight option, none of which change.
  • The "Show time at midnight" help text now mentions that the setting also covers a 11:59 PM range end.

v1.19.0

Choose a tag to compare

@github-actions github-actions released this 01 Sep 21:13

Highlights

  • Tags used on pages and query loop items that don't resolve to a post ID now return a context-appropriate result!

    • Current-context Title tags on archives, search results, 404 pages and the blog home return the archive label, date span, search heading, or site name, not the query's first post's title. (Fixed)
    • The Content tag now shows a post type archive's registered description. (Added)
    • On a 404 page, Title and Content borrow GeneratePress's "not found" text, falling back to WP's. (Added)
    • A Try Text tag can fall back to the page's own heading, so one tag works site-wide across custom headings and archive/search/404 titles alike. (Added)
    • Tags inside a query loop over terms or users now read that term or user, not a post that happened to share its ID. (Fixed)
  • One trade-off: Tags used in a WooCommerce product loop previously worked by coincidence, but now render nothing. We plan to address this in a future release. (Changed)

  • When another plugin registers one of this plugin's tag names, the settings page now names which plugin's version of the tag is actually active and the file it was registered in, so the report names something you can go and deactivate. (Added)

  • Calling bws_get_loop_row_context() from your own code? It is bws_get_loop_item_context() now, with no compatibility alias. Search any custom code for the old name before you update. (Changed)

Added

  • The Content tag now shows a post type's archive description. On a post type archive, {{content}} renders the description set on the post type's registration, formatted the way WordPress formats it. It had rendered whatever post led the archive's query (see Fixed below).
  • On a 404 page, the Title and Content tags borrow the theme's own "not found" text. With GeneratePress active, {{title}} renders the heading GP's 404 template shows, including any customization a site made through GP's generate_404_title filter, and {{content}} does the same with generate_404_text. Without GeneratePress, the title falls back to WordPress's own "Page not found" and the content renders nothing.
  • A Try Text tag can fall back to the page context's own heading. {{text use:title}} and a try_text attempt set to the Title field now resolve on archives, search results, 404 pages and the blog home, returning the same value as {{title}} there. A try_text tag reading a custom heading field first and the Title field second gives a site-wide header that uses an author's custom heading where one is set and the context's canonical title everywhere else, which {{title}} alone cannot express.
  • A tag name this plugin shares with another plugin is now reported, in both directions. The base tags deliberately register over a name another plugin already holds, because standing down would make {{text}} silently cease to exist on every page using it. That overwrite now announces itself through WordPress's standard incorrect-usage channel, naming the tag and the other plugin, instead of happening quietly. The other direction is the one that could not be detected at all: a plugin registering one of these names after this plugin's registration pass takes the tag over on every block already using it, and nothing anywhere said so. A late re-read now catches that and reports it the same way. Both reports need WP_DEBUG and go to the debug log; the Tag Name Conflicts subsection below is the surface that does not.
  • A documented tag of this plugin's that does not exist on your site is now reported too. The term_ and try_ tags stand down from a name another plugin already holds rather than registering over it, and until now they did it silently: the tag was simply absent from the editor's tag list, with nothing anywhere saying why, and documentation describing it did not apply on that site. It is now recorded and reported like the two directions above. A stand-down is not a guarantee that nothing changed. It is recorded on every request once the other plugin is present, including on a site whose blocks were already using this plugin's tag under that name; those blocks now render through the other plugin's code, carrying settings it never defined. What the report tells you is which plugin answers for the name, not whether your output moved. This is live on any site running GB Query Enhancements, which holds term_title.
  • Settings > Tag Extensions gains a read-only Tag Name Conflicts report, under Diagnostics. It lists any tag name that collided on that page load, which plugin's version of the tag is the one actually active, and the file that plugin registered it in. The plugin is named from that file, so it names something you can go and deactivate rather than the other tag's label. It changes no setting and no output. It exists because the developer-facing reports above need WP_DEBUG and a log file, which is not where a site owner debugging a live install will look.
  • For plugin developers: bws_loop_item_is_post_or_row() is a new published helper. It answers whether the current query-loop item is one a post-meta or repeater read can be served from: true for a post and for a repeater row, false for a term, a user, and an item whose shape is not recognized. That is the question the in_loop flag used to answer by accident, so this is what you want if you branch on in_loop alone before skipping a "no entity, give up" bail. See docs/plugin-integration.md §Field helpers.

Changed

  • Tags inside a WooCommerce product loop now render nothing. A query loop's item is now identified by its shape, and four shapes are read: a post, a term, a user and a repeater row. WooCommerce's product loop hands over a shape matching none of them, and an item this plugin cannot identify resolves to nothing rather than to whatever the surrounding page holds. Those loops had been rendering the right thing by arithmetic coincidence, since a product ID is also a post ID. That same coincidence is what made term and user loops read an unrelated post (see Fixed below), so a shape we cannot identify saying nothing is the point of the change and not collateral damage from it. Restoring product loops is planned for a future release.
  • For plugin developers: bws_get_loop_row_context() is now bws_get_loop_item_context(), and the row_post_id key it returns is now item_post_id. There is no compatibility shim, no alias and no deprecation notice, so a call to the old name is a fatal error rather than a warning. Search your own code for bws_get_loop_row_context and row_post_id before updating. The old names said "row", which stopped being true once the helper began reporting terms and users as well as posts and repeater rows.
  • bws_get_loop_item_context() reports more than it used to, and in_loop no longer means what it did. Two keys are new, item_kind and item_id, carrying the loop item's own entity. The post ID key still holds a post and only a post, so its meaning did not move; only its name did, which is the rename above. What changed is in_loop: it is now true for a term, a user and an unrecognized item, where before it meant "in a loop with a post or a repeater row behind it". Code branching on in_loop alone to decide whether a post-meta read can serve is now wrong on three item kinds. If you want the old meaning, bws_loop_item_is_post_or_row() is it, rather than a rewritten condition of your own.

Fixed

  • Bare tags on archives, search results, 404 pages and the blog home no longer read an unrelated post. On a date archive, a post type archive, a search results page, a 404 and a latest-posts blog home, WordPress leaves the query's first post where "the current post" is looked up, so {{title}} there showed that post's title and {{content}} could print that post's entire body onto the page. Each of those contexts now resolves its own answer, the same one WordPress core uses for the browser tab title: the archive label on a post type archive, the date span on a date archive, the formatted "Search Results for ..." heading on search, and the site name on the blog home. Tags with no sensible value in these contexts, a permalink or an image or a date field for instance, now render nothing rather than a wrong post's value; the same holds inside try_ tag attempts. An explicit source or a query loop still wins, exactly as before. This is the same defect class as the term and user loop fix below, reached through the page's main query instead of a loop.
    • One page may change on purpose: {{content}} on a latest-posts blog home used to render the first post's entire content through that leak. A home page built on that behavior now renders nothing there; a query loop showing the lead post is the supported way to get it back.
  • Tags in a query loop over terms or users now read the loop's own term or user. They had been reading a post instead: a third-party query extension hands the loop item's ID to a GenerateBlocks filter that cannot say what kind of thing the ID belongs to, and this plugin took it for a post ID. Term IDs and post IDs collide constantly, since every fresh install has both a term 1 and a post 1, so {{title}} in a term loop showed some post's title, plausibly and wrongly. Recognition reads the loop item's own shape, so the fix holds for any extension supplying loop items, not only the one it was found on.
  • Fields read from a TRASHED post inside a query loop no longer render. Version 1.18.0 made tag output status-aware, and one path was missed: a tag reading a meta field off a query loop's row went straight to the field, whatever the row's status was. In an ordinary GenerateBlocks query loop the effect was narrow, because GenerateBlocks already declines to run a loop over non-public posts for anyone who cannot read private posts. What got through was trashed content, shown to users who can, and t...
Read more

v1.18.0

Choose a tag to compare

@github-actions github-actions released this 21 Aug 21:11
e65f2fa

Highlights

  • Tag output is now user- and status-aware: fields from drafts and private posts only show for users who have access to those items; fields from trashed posts no longer appear for anyone. If a spot on your site goes blank after this update, check whether the tag reads from an unpublished or trashed post. (Changed)

  • Content, Permalink and Image tags that read through categories or tags now show the first term only. A value stored on a later term no longer appears, so if one of these tags goes blank, check whether the post has multiple terms with varying fields populated. (Changed)

  • The Migration Tool stops listing image tags it cannot convert, so a list that would never clear now clears. (Fixed)

Changed

  • Unpublished content no longer feeds tag output for readers who cannot see it. Draft and private posts stop resolving for visitors, on every tag; an author previewing their own draft still sees it. Trashed content resolves for nobody, including administrators, since WordPress shows a trashed page to no one. A source chain that passes through an unreadable post stops there, even when the chain's final target is public.

    This particularly affects tags using reference/relational field source path steps. An ACF Relationship or Post Object field set to return Post IDs passes every stored ID straight through, so drafts, private posts, trashed posts and references to deleted posts had all been treated as usable sources. An ACF field set to return Post Objects already hid trashed and deleted targets, but not drafts or private posts. After this update the tag output is the same for either return format; the check applies to the post a tag has resolved, regardless of how it was stored or returned.

    However, visibility awareness applies to whatever post a tag resolves, including the post the block is rendering in a loop or template, and to each step of a multi-step source. Tags that read a term or a repeater row are unaffected, since neither has a published state of its own.

  • Content, Permalink and Image tags pointed at a post's terms now read only the first term from the selected taxonomy. What it buys: a title and an image tag pointed at the same post's terms in a given taxonomy will come from the same term, rather than potentially showing the title from the first term and an image from another term (because the selected image field was empty on the first term).

    The try_ versions inherit this per attempt: an attempt reading through a taxonomy stops at the first term instead of searching the rest, so it hands off to the next attempt sooner. The overall attempt and fallback usage logic is unchanged.

    ("First term" here is the first term which WordPress returns for that post in that taxonomy, which is alphabetical by name unless a plugin changes it, not based on the order they were assigned to the post.)

  • The limit control is no longer offered on the Content, Permalink and Image tags, including their try_ variants. These tags show one result, so there is nothing for the control to bound, and every limit saved in such a tag is ignored at render. A previously saved limit stays in the tag untouched. On the three tags themselves, a note now appears under the source controls when the source configuration can match more than one item, stating that only the first item is read. The try_ variants do not show that note yet.

  • The limit controls now say what the number counts. A limit selects how many items are read from that source step, and an item whose field is empty keeps its place rather than being replaced by the next one; the help text now states that. Each step's limit is labelled for what that step returns: Limit Posts Read on a relationship step, Limit Terms Read on a taxonomy step, Limit Repeater Rows Read on a repeater step.

  • The field configuration note no longer predicts several results on the Content, Permalink and Image tags, including their try_ variants. It still says the field holds multiple values; it stops claiming they will all render where only one can.

  • Image tags saved before 1.16.0 now get their image size written into the Return As setting. Such a tag says only as:url, while the editor shows Full Size as selected. Affected tags will be listed by the Migration Tool on scanning, allowing you to bulk-rewrite them to as:url,full. Nothing renders differently: a tag with no size in it already rendered at full size.

Fixed

  • A source step limit no longer counts deleted items. A stale reference left behind in a field, or a removed post still named by stored data, is skipped without counting against the limit, instead of bumping usable items.
  • A limit inside a try_ attempt now applies to the step it is written on. A number written on one step no longer bounds a later step's output after another step is added behind it.
  • try_datetime_ tags now accept a site source per slot, which 1.15.0 said every Try tag did. {{try_datetime_single}} and {{try_datetime_range}} were left out when the other five Try tag families got their site slots, so Site never appeared in their slot source lists. A date can now read the current post first and an options page second. The field key is set once on the tag rather than per slot, so a site slot reads that same key from the site store.
  • try_email and try_phone slots set to Site now read the site value in every case. A slot whose Site source carried anything extra, such as a limit written onto the source step, was not recognized: the slot read the current post's field instead. That wrong value was plausible enough to count as a successful attempt, so the remaining attempts in the chain never ran. The slot now dispatches on what the source resolves to, and an attempt whose source cannot resolve is skipped rather than answered from the wrong place.
  • The Migration Tool no longer lists image tags that have nothing to convert. Every {{image}}, {{term_image}} and {{try_image}} tag was reported as needing the image size conversion, whether or not it still carried the old separate size setting that conversion exists to fold in. Running the tool left those tags untouched, so the next scan listed the same posts again and the list could never be cleared. Only a tag that still holds an old size setting is listed now. Present since 1.16.0.

v1.17.0

Choose a tag to compare

@github-actions github-actions released this 19 Aug 14:48

Highlights

  • A tag's source can now follow more than one step. Start at the current context, the site or a relationship, then follow further relationships or drop into a post's taxonomy terms to read the field you need. (Added)

    • 25 retired tag names for fixed two-step source paths are now convertible via the Migration Tool.
  • A tag you saved with a result limit of 0 or -1 now correctly returns every result; change it to 1 to keep the previous output. (Changed)

  • A source the tag cannot use renders nothing, instead of falling through to the current context. If a spot on your site goes blank after this update, check the tag's source(s). (Fixed)

  • The first-available tags read the author on an author archive now. An existing tag that output nothing or a later attempt instead may now show the author's name or bio. (Fixed)

  • Multi-slot tags now have buttons to add and remove slots, instead of automatically showing the next slot's controls. (Changed)

Added

  • A tag's source path can now follow more than one step. On the base tags, the Source control builds a path: start at the current context, the site or a relationship, then add steps that follow a relationship field or drop into a taxonomy term. Before this a source could follow one relationship and one taxonomy and no further, so something two relationships away was out of reach.
    • Each step that can return several results now has its own Limit Results control. Limit a taxonomy step to one and you get one term per referenced post, not one term overall. Leave it blank for all. Once an earlier step returns several results, a subsequent step's limit applies to each of them in turn. Existing tags render as before, apart from the three source items under Fixed.
    • A relationship field stored on an options page can now be followed. A path that starts at the site can take a relationship step, where before that step returned nothing. A globally configured "featured partner" now reads the way a per-post one does.
  • A slot's source can follow a path too, the same one a plain tag can. Each field in {{join}} and each attempt in the first-available tags builds its source with the same control a base tag uses, and it now resolves the same way: start at the current context, the site or a relationship, then follow a relationship field or drop into a taxonomy term, as many steps as you need, each with its own Limit results. A slot used to be held to one relationship step and one taxonomy step, and a source that went further rendered nothing at all; the editor did not offer those steps for that reason, and now it does. "Same as Previous Source" carries the whole path, so an attempt that inherits a two-step source inherits both steps rather than landing back on the current context. Setting a step of your own on the same kind still replaces the inherited one, as it always has.
  • 25 retired tag names are now convertible. The second_related_post_* and post_term_related_post_* families were retired in 1.6, and the Migration Tool has had no conversion for them since, because nothing could follow two relationships. A source path can. This is the one change here you may see on a page: these tags have rendered nothing since 1.14, so a blank spot can start showing content again after you run the migrator. A tag whose second relationship field was never filled in is left alone rather than half-converted.
  • Another plugin can add its own starting point to the Source control. A plugin that resolves its own context from the page being viewed can offer it as a place a source path starts. It then appears in the Source list on every base tag and in every slot of {{join}} and the first-available tags, named by that plugin, next to Current and Site. Steps, per-step limits, field pickers and previews all work from there as usual. Nothing appears until a plugin you have installed opts in.
  • For plugin developers: two routes to offering a source as a source-path start. A source class returns true from is_selectable_root(), or a plugin declares one from the bws_dynamic_tags_chain_roots filter with a label, a context type and a function that finds the entity, no class needed. Offering is separate from resolving: a tag naming your source resolves whether or not you currently offer it, so removing the offer alone never blanks saved content. Upgrade note: is_selectable_root() is declared on SourceInterface, so a source class implementing the interface directly must add it; extending AbstractSource inherits the default. See docs/plugin-integration.md §1a.
  • The editor now tells you what a relationship field does and does not enforce. Pick an ACF relationship or post object field for a source step and a short note appears above Limit results. It says whether the field has a configured limit, whether edits made elsewhere can add to it, and whether it is set to hold a single entry. ACF applies a configured limit only when the field is edited directly in ACF, so a bidirectional write, an import or WP-CLI can push a field past it; and a post object field set to a single entry can be holding entries it never shows, all of which appear at once if you later allow it several. Field group settings are where you would otherwise have to check this, and they are out of reach in Patterns, Elements and templates, which is where the note is most useful. It reads field settings only, never stored values, so it says the same thing wherever you are. A field with nothing to report gets no note, and the note never blocks a save or changes what a tag renders.
  • For plugin developers: one call migrates a prefixed tag family onto the base tags. bws_register_modifier_root_migrations( 'yourprefix', 'yoursource' ) registers one migration entry per registered tag template, so there is no list of tag names to keep in step. The Migration Tool then rewrites saved content: a tag that followed a relationship or dropped into a taxonomy term keeps doing exactly that, as steps of a source path. Your tags stay registered and go on rendering, so retiring the prefix stays your decision. One shape renders differently after conversion: a prefixed tag set to read the site that also carried a relationship or taxonomy setting has rendered nothing at all, and converting it drops both and gives you the site value. The migrator reaches every post, page and template part, but not block widgets. See docs/plugin-integration.md §9.
  • For plugin developers: the plugin now announces when it writes post content without firing a save. The Migration Tool writes migrated content straight to the database on purpose, so that a maintenance run does not create a second revision, bump modified dates, or set off every save listener on the site. The cost is that nothing downstream hears about it, which is how the pattern cache under Fixed went stale. bws_dynamic_tags_content_written now fires with the post ID and the new content, so a cache of your own over post content can refresh itself. Use the content you are handed: no post object is passed, because the one in scope at that point still holds the pre-migration content. It states a fact rather than promising to drive anyone's cache. See docs/plugin-integration.md §10.

Changed

  • Multi-slot tags now configure one slot at a time, with buttons to add and remove them. {{join}} and the nine try_ tags used to reveal their slots one at a time: slot 3's controls appeared only once slot 2 held a value, and clearing an early slot could hide the later ones. Each slot is now a block of controls you add and remove directly, and removing a middle slot closes the gap without re-pointing the slots after it. Each slot saves under one option key instead of six, and its key is a letter (A, B, C), which is also its panel label; {{join}} adds a field (Field B), a try_ tag an attempt (Attempt C). Existing tags are converted for you, both on opening one in the editor and on running the Migration Tool. Output is unchanged apart from the source fixes under Fixed.
    • {{join}}'s format tokens are letters now, matching those labels. In template mode you write %A, %B, and so on, where 1.15.0 used %1, %2. The numbered tokens still work and will keep working, so a format string you already wrote requires no change. One thing to know if you used a literal percent sign: a % directly before a capital letter now reads as a slot token, so text like 10%APR would change meaning. The Migration Tool rewrites those to %%APR, which renders as typed; type %% when you mean a literal percent before a capital letter.
  • Every field in a field selector now shows its type. A row reads Partner Staff (Relationship, 'partner_staff'), where before it read the label and key alone and the type was reachable only by narrowing the type filter. Typing a type name searches on it, so "post object" finds the post object fields without touching that filter. The type is read from the field itself, so it is spelled the same way on every field, including types this plugin has no name for and any a third-party plugin adds. Order is unchanged: still alphabetical by field name, not grouped by type. A field with no type, such as a plain registered meta key, shows no type.
  • A tag whose source is a path no longer shows the two old source fields as well. "Relationship Field Key" and "Get from taxonomy term?" are steps in the path now, so the separate controls are gone from the base tags. A tag saved the old way opens with its relationship and taxonomy showing as steps, and saves and renders as before unless it is one of the shapes under Fixed. The term_ family and {{call}} keep both controls.
  • Tag option controls are grouped in the editor. Control groups such as the source path, the field selection, and how the value is ...
Read more

v1.16.0

Choose a tag to compare

@github-actions github-actions released this 23 Jul 20:57
1c63932

Highlights

  • Stray characters no longer appear at the start of plugin output. An invisible byte-order mark in one plugin file printed on every page load, breaking WP-CLI scripts that read a command's value and risking "headers already sent" warnings. (Fixed)
  • Tag options are now ordered for reading in the panel and for scanning in the tag string. Controls run source, then formatting, then link, then fallback, so you pick what to read before how to show it; the saved string leads with the return type or format instead, so it is visible up front when you copy a tag. Multi-slot try_ tags keep each slot's options together. (Changed)
  • Image size now belongs to the URL return type, and shows only when you pick it. Size appears under Return type only for URL, the one mode that uses it, and the two travel together in the saved tag as as:url,medium. Size labels also read properly now and include custom sizes your theme registers. (Changed)

Added

  • {{text}} now reads the author on an author archive, joining {{title}} and {{content}} (1.15.0). With Use Entity Title it shows the display name; with a field key it reads the author's user field. {{join}} slots pick this up too, so a join renders on author archives. try_ slots do not yet.

Changed

  • Tag option controls now read source first, then formatting, and the saved tag string leads with the formatting. The editor panel now consistently orders controls as source and field, then formatting, then link, then fallback, so you choose what to read before how to show it. The saved tag string is ordered separately: the return type or format leads (so it is visible up front when you copy a tag), followed by source, link, and fallback. For a multi-slot try_ tag, each slot's options now stay grouped together in the string instead of scattering when you revise an earlier slot. Existing tags are re-ordered to match the moment you open them in the editor; output is unchanged.
  • {{join}}'s fallback and separator options are renamed to match the rest of the plugin. The fallback is now fallback (it shipped in 1.15.0 as fallback_text, the legacy name every other tag had already moved away from), and the separator is now written valueSep in the tag string (freeing the shorter sep name for the list separator the other tags use when a source returns several values). Both controls and their behavior are unchanged; only the saved keys differ. Because {{join}} is one release old, these are plain renames with no migration: a {{join}} tag that has a custom Fallback Text or separator set needs it re-entered. A tag using the defaults is unaffected.
  • Image size now belongs to the URL return type, and shows only when you pick it. The image tags carry one Return type control (URL, ID, Alt Text, and so on); the size dropdown now appears under it only when the return type is URL, which is the only mode a size affects. Picking Alt Text or ID hides it, since those never use a size. The two now travel together in the saved tag as as:url,medium. Size labels also read properly now (for example "Medium Large" rather than "Medium large") and include any custom sizes your theme registers. Existing image tags keep rendering unchanged; run the Tag Converter to fold their saved strings into the new form. A tag saved before this release still carries a separate size: in its string until then, and output is unchanged either way.

Removed

  • Removed seven option-builder functions that no longer fed any tag (bws_get_content_options, bws_get_custom_text_options, and the four bws_get_date*_options builders for the retired date/datetime templates), along with an unused helper method on the source base class. These were internal leftovers from templates retired in earlier releases. bws_post_term_extraction_options() is kept, since external plugins can use it, and its fallback option is renamed to fallback to match the documented contract.

Fixed

  • {{text}} no longer repeats the fallback text for each empty item in a list. Reading a list (several taxonomy terms, or several related posts) where only some entries had a value inserted the fallback in place of each empty one, so a three-term list with one value rendered Sales, N/A, N/A. The fallback is a stand-in for the whole tag, not for one item, so it now renders only when nothing at all resolves, and the list shows just the values that exist. A single empty result also no longer picks up a link that belonged to a real value. Behavior for a tag reading one value is unchanged.
  • Stray characters no longer appear at the start of plugin output. One plugin file carried an invisible byte-order mark, which PHP treats as content and prints on every page load. The visible effect was on WP-CLI: the first line of output from any wp command was prefixed with the stray bytes, which broke scripts that read a command's value directly. The same bytes were sent before any redirect or JSON response, so this also removes a latent cause of "headers already sent" warnings.

v1.15.1

Choose a tag to compare

@github-actions github-actions released this 20 Jul 20:40

Fixed

  • WP-CLI commands no longer fail on an installed copy of the plugin. Running any wp command with 1.15.0 active stopped WordPress before it finished loading, so unrelated commands (wp search-replace, for example) exited without doing their work. The plugin was loading a development-only file that is not part of the released download. Released builds now skip it, and the wp bws render-tag development command still loads when the file is present.

v.1.15.0

Choose a tag to compare

@github-actions github-actions released this 20 Jul 19:08

v1.14.0

Choose a tag to compare

@github-actions github-actions released this 08 Jul 16:12

Highlights

  • Base tags now read the term on a taxonomy archive. A bare {{title}} / {{content}} / {{permalink}} on a category, tag, or custom-taxonomy archive reads the term itself instead of an arbitrary post. (Added)
  • Deprecated tags no longer register with GenerateBlocks. The old tag names are gone from the picker; the Migration Tool still finds and updates existing content that uses them. (Removed)
  • {{text}} / {{title}} with src:ref now list every related post, not just the first, when you raise the result limit. (Fixed)

Added — base tags resolve the term on a taxonomy term archive

  • On a category, tag, or custom-taxonomy term archive, a bare base tag now reads the term itself. {{title}} shows the term name, {{content}} the term description, {{permalink}} the term URL. Previously a base tag on a term archive read from an arbitrary post (whatever the main query listed first), so it showed the wrong thing. Now a bare tag follows its context: the term on a term archive, the row post inside a query loop, the current post on a singular page.
    • Term reference/relational fields are also supported. A src:ref tag hops from the resolved term, reading its relationship field off the term instead of off an arbitrary post.
    • Per-tag term reads: {{title}} → term name, {{content}} → term description, {{permalink}} → term URL, {{text key:…}} → a term meta/ACF field. {{image}} stays empty on a term (a taxonomy term has no intrinsic image); set a key to read a term image field.
    • try_ chains match. A try_text / try_title / try_content / try_permalink slot on a term archive resolves the term just like the standalone tag, so a fallback chain behaves the same whether it runs on a post or a term archive.
    • This covers taxonomy term archives only. Other archive types (the blog/posts index, search results, date, author, and post-type archives) are not yet context-aware: a bare base tag there still reads the first listed post, unchanged from before. Expanding proper resolution to those contexts is planned for a later release.

Added — pre-upgrade warning when deprecated tags are about to stop rendering

  • The Plugins and Updates screens now warn before an upgrade removes the old tag names. When the available update is the one that stops registering the deprecated tags, the plugin's update row adds a caution line pointing you to the Migration Tool, so you can scan and fix affected content before updating rather than finding raw tag strings in your content afterward. The notice only appears on the upgrade that actually removes them and goes away once you are past it.

Changed — bare tags on a term archive no longer read a stray post

  • Tags that have no term reading resolve to empty on a term archive instead of showing an arbitrary post's data. The same context fix that lets {{title}}/{{content}} read the term also stops the tags that can't read a term from silently reading whatever post the archive listed first. On a term archive: {{datetime_single}} / {{datetime_range}} (which read post/site date fields, not term fields) and {{call}} (which needs a post) now return empty or their fallback rather than the first-listed post's value; a bare srcTermIn tag (which hops a post to its terms) likewise resolves empty, since a term archive has no post to hop from. This is the honest result where before the output came from an unrelated post. Reading term date fields is planned for a later release.

Changed — deprecated-tag settings split into Deprecated and Removed, with scan-aware hiding

  • The settings page now sorts deprecated tags into two boxes: Deprecated Tags and Removed Tags. A tag is "removed" once it no longer renders (its GenerateBlocks registration is gone), and "deprecated" while it still registers. All of this plugin's old N×M tags are Removed; context-modifier aliases registered by companion plugins (for example the portal tags) stay Deprecated while the current tag they point at is still live. The Keep/Suppress/Disable control stays on the Deprecated box for tags that still register.
  • Options split the same way. Deprecated Options lists the option-key corrections still applied to current tags; a Removed Options box is reserved for the future point when an old option key is dropped from the reading code entirely. It is empty today.
  • The whole group moved above Diagnostics, next to the Migration Tool it works with.
  • Boxes now hide entries that were not found in your content. After a plugin upgrade or a Migration Tool scan, only the deprecated or removed tags and options actually present in your posts are listed, so the page reflects what you have rather than every name the plugin knows. Migrate a post and its old tag drops off the list on the next scan. The Migration Tool itself is always shown.
  • A "Show all" toggle in Diagnostics lists every registered entry regardless of scan results, for auditing or if a scan looks wrong. It stays on until you turn it off.

Changed — internal: traversal pipeline replaces the source-class matrix

  • Base and context-modifier tags (term_*, view_*) now resolve through one data-driven pipeline instead of a per-combination source class. A single source factory works out the starting point (term, post, loop row, or site) and generic traversal steps handle the src:ref relationship hop and the srcTermIn term hop. Resolution is unchanged for existing content; this retires the N×M source-class growth and is what lets base tags become context-aware. The related-post source classes stay registered for the deprecated tag names that still use them.
  • traversal_source_key is now accepted-but-ignored in register_modifier(). External plugins registering a context modifier no longer need a custom traversal source class — the src:ref hop is generic. Existing registrations pass the key unchanged and keep working; it may be dropped from new ones. See plugin-integration.md. Verified against bws-portal-system: no portal changes required.

Removed — deprecated tags no longer register with GenerateBlocks

  • All deprecated tag names (old N×M source×template tags, plus the eight pre-1.6.0 renames) are gone from the GB tag picker and no longer render. They were already flagged deprecated in the editor and documented as due for removal; this completes that removal instead of leaving them registered indefinitely.
  • The admin Migration Tool (Settings → Tag Extensions) still finds and fixes them. Scan and Migrate keep working exactly as before, so existing content with an old tag string gets a clean, correct upgrade path to its current equivalent — only the live rendering of the deprecated name itself is gone.

Fixed — {{text}} / {{title}} with src:ref now list every related post

  • A related-field source with a raised limit now returns all matching posts, not just the first. {{title src:ref|ref:related_vendors|limit:5}} lists up to five titles joined by the separator; before, it silently returned only the first related post even though the Result Limit and Result Separator controls were offered. The controls now do what they advertise. Default limit is 1, so a tag without an explicit limit is unchanged.

Fixed — datetime_single / datetime_range no longer serialize the default source

  • Switching a datetime tag's Source away from and back to "Current" no longer leaves src:current written into the saved tag string. Every other base tag strips its default select value at registration so the default token never gets serialized; datetime_single and datetime_range built their options through a dedicated function that skipped this step, so round-tripping the Source dropdown left a stray src:current (harmless functionally, but inconsistent with every other tag's wire format). No user-facing behavior change — output is identical either way.

v1.13.0

Choose a tag to compare

@github-actions github-actions released this 06 Jul 19:55
10d7510

Added — smart field selector (replaces blind key typing)

  • Every meta/option field key input is now a searchable field picker instead of a blank text box. The key, ref, linkKey (link URL field), and all six datetime key inputs (plus their try_ per-slot versions) list the registered fields on your site — ACF fields, their sub-fields, options-page fields, taxonomy-term fields, and core registered meta — so you pick a field instead of remembering its key. It works in any editor context, including WP Patterns, GP Elements, and templates, where GB's own selector shows nothing because it can only read the post you happen to be editing.
    • Two filters narrow the list. Filter fields by location drills through a path — Post fields › Client Details › Coverage Options (repeater) — so you can jump to exactly the group or repeater you mean; container fields are flagged (repeater) / (group). Filter fields by type narrows to a field type (Date, Email, Relationship, …) or to fields usable inside a loop. The location filter auto-presets from the tag's own source (a srcTermIn tag opens on term fields, src:site on site fields) but never assumes the current post is the target — you can always override.
    • The control label follows what you pick. Narrow the location to a group and the label reads "Client Details Field"; narrow to a source and it reads "Post / Term / Site Meta Field". Datetime and relationship keys keep their specific labels.
    • Type any key you like. Unregistered keys (a plugin's raw meta, a key you know by heart) still work — start typing and choose Use custom key: "…" to commit it. A clear (✕) button empties the field. There is no separate "Add" step to forget.
    • Same-named fields are handled honestly. A field key that appears in more than one field group collapses to one entry that shows under every location it belongs to; two genuinely different fields that share a key but have different labels (a person's "Name" vs a repeater row's "Feature Name") stay as separate, distinguishable entries.
    • Only fields the tag can actually read are offered — the list is filtered through the same security gate the tag resolver enforces, so it never lists a key that would refuse to resolve.
    • The field list is assembled once per editor load and inlined into the page, so opening a tag never waits on a network request.

Fixed

  • A custom key that is a substring of a listed field label can now be committed. Typing a raw key like city no longer suppresses the Use custom key: "city" option just because a field labelled "City ('venue_city')" is in the list; the escape hatch now triggers on an exact key match, not a substring-of-label match.
  • A key that differs only in letter case from a listed field is now committable. Meta keys are case-sensitive, so typing event_date when an Event_Date field exists now offers Use custom key: "event_date" instead of silently steering you to the differently-cased field.
  • A malformed field envelope no longer breaks the editor. If the inlined field list fails to JSON-encode (malformed UTF-8 in an ACF label, or a very deeply nested repeater), the page falls back to an empty object and fetches the list over REST instead of emitting an invalid inline script. Field labels are also escaped so a label containing markup cannot break the editor page.
  • Meta keys registered for a specific post type or taxonomy now appear in the picker. Previously only globally-registered meta was listed; a key registered for one post type (or taxonomy) is now offered too, matching what the tag can actually read.
  • A site-wide registered meta key is no longer hidden by a same-named custom field. A global registered key stays in the list even when one post type also defines a field of the same name, so you keep the key on the post types where only the registered one applies.

v1.12.0

Choose a tag to compare

@github-actions github-actions released this 30 Jun 13:27
906d9eb

Added — {{call}} function-passthrough tag (for developers)

  • New {{call}} tag runs a site-defined PHP function and outputs what it returns. Some display values are too conditional for base tags to assemble (a function that branches on a term name, formats a score, looks up an indicator). {{call}} hands that work to a PHP function you write, binds the loop-correct post for it, and prints the returned string. This is a developer tool: it ships empty and produces nothing until you allowlist a function — every other tag works out of the box; this one does not, by design.
    • Allowlist in code, not the database. Register a function via the bws_fn_passthrough_functions filter or the bws_register_call_function( 'my_fn' ) helper. The trust boundary is file/code access only; {{call}} grants editors no capability a developer didn't already hold in PHP. A security gate refuses anything that isn't a real, non-built-in function (so system, unlink, and friends can never be called).
    • Post-context only. The source menu offers Current and In Reference/Relational Field — both resolve to a post the function receives as its first argument. This fixes the Query Loop case where ambient get_the_ID() is wrong or empty (e.g. a relationship-field loop). Site and taxonomy-term sources are intentionally not offered: a $post_id function can't consume them.
    • Optional single argument. An Argument field passes one value (e.g. a format like short or Y-m-d); left empty, the function's own default applies.
    • Output is verbatim and unescaped — the function owns its own escaping (real functions return trusted display HTML). If the function is missing, unavailable, errors, or returns nothing, the tag outputs its Fallback instead; a thrown error is always logged server-side and never leaks to the page.
    • Read-only allowlist mirror under the BWS Dynamic Tags settings shows which functions {{call}} will accept and their status. The editor's function dropdown is populated from the same allowlist.
    • Known limit: flat ACF repeater rows (no underlying post) are not yet supported; the related-post and current-post loop cases are.