Skip to content

Releases: Mtuozzo86/hozio-dynamic-tags

v4.20.7: Updates install unattended, dev-URL banner can be dismissed

Choose a tag to compare

@mtallo22 mtallo22 released this 22 Sep 19:25

Two fixes, both about the "2f" domains that kept coming back.

Sites were not installing updates on their own. The updater hooked itself only on wp-admin screens, but WordPress installs updates unattended from WP-Cron, where that test is false. During the one run that can actually install something, neither the update check nor the approval filter existed, so the plugin declined its own update - while wp-admin, where the check did run, kept reporting an update was available. Sites left behind on 4.20.5 still carry the old hostname bug, which is why "2f" domains kept being seen after 4.20.6 shipped. The updater now also registers during cron, WP-CLI and REST. An ordinary page view still registers nothing, and the GitHub release is cached for 12 hours.

The red dev-URL banner can now be dismissed for the rest of the browser session, instead of appearing on every page load. It returns if a later scan finds a different number of rows, and the toolbar item always stays. Dismissal is entirely client-side in a session cookie; the server never varies its output on it, so a cached page can never be served to someone else already dismissed.

Verified: 416 checks pass, including the dismiss script executed against a DOM and the updater's hook registration in each context.

v4.20.6: Dev URL fix no longer disconnects Elementor ACF fields

Choose a tag to compare

@mtallo22 mtallo22 released this 14 Sep 20:47

Dev URL Guard: "Fix now" no longer disconnects ACF fields on Elementor sites.

  • Elementor stores dynamic tag settings URL-encoded; the scan read the "2F" of %2F as part of the hostname ("2fsite.mystagingwebsite.com") and Fix removed it, leaving settings Elementor could not decode. Hostnames can no longer start inside an escape sequence, and encoded links keep every escape byte for byte.
  • Rows damaged by an earlier Fix are detected (shown as "Broken by an earlier fix") and repaired when you press Fix now. Nothing is written automatically.
  • Legacy "2f..." hostnames in reports and undo records are cleaned so Undo cannot write them back.
  • Elementor CSS/element cache is cleared after a fix or undo that changed rows.

Verified: read-only dry run on millermarineservices.com (26 rows, 0 dev hosts left, 0 broken tags), 222 new tests plus all existing suites passing.

v4.20.5: Stray-bracket filter no longer corrupts inline JavaScript

Choose a tag to compare

@mtallo22 mtallo22 released this 10 Sep 15:31

The stray-angle-bracket content filter was corrupting inline JavaScript on every page that had any.

What was wrong

hozio_escape_stray_angle_brackets() ran over the whole rendered post with no exemption for <script>, <style> or HTML comments. Anything inside a script matching its > rule was encoded:

In your script What rendered
item => item.id item =&gt; item.id
a->b a-&gt;b
return (x)>y return (x)&gt;y

One escaped character is a SyntaxError, and the whole script dies without a word in the page.

On angeloakspetcrematory.com that killed Gravity Forms' AJAX bootstrap and its iframe response handler: the submission lock never released, so every Continue and Save button showed an endless spinner.

The filter runs at priority 20 — after do_shortcode, and after Elementor injects its builder output at priority 9 — so it saw every script the page produced. That is why the reach was so wide.

What changed

<script>, <style> and HTML comments are exempt and come back byte for byte. Content holding an unclosed <script> or <style> is left completely untouched, because a partial block cannot be located reliably.

The hook, its priority, and the function name are unchanged.

Prose behaviour is verified unchanged, not assumed

The two rules are byte-identical to the previous ones. A differential test runs the shipped function against the original across 40,050 inputs — real markup, prose, entities, shortcodes, multi-byte text and randomised fuzzing — and asserts byte-identical output on every one. 18,052 of those are inputs where the rules actually change something.

Cost on a 290KB Elementor page: +0.97ms. Plain prose is unchanged.

One deliberate divergence from the brief

The exemption matches the blocks as an alternative inside the same pattern, rather than splitting the content first.

Both rules use lookbehind and lookahead. Splitting evaluates them against segment edges instead of the real neighbouring characters, which silently changes prose output next to a block. )><script> is the proof:

  • Old code: leaves the > alone, because a < follows it.
  • A preg_split-based fix: escapes it to )&gt;.
  • This fix: leaves it alone, matching the old code.

That case is now a test.

Also

Guards the original lacked: a non-string (or null) is returned untouched, and a regex-engine failure returns the content rather than the null preg_replace hands back on failure — which the old code passed straight through as the post content, blanking it.

New hozio_angle_bracket_fix_enabled filter turns the whole thing off for a site that needs it off.

Testing

156 assertions across five suites plus 19 environment scenarios, all passing; 35 files lint clean on PHP 8.2.

v4.20.4: Toolbar drop-downs no longer open behind the banner

Choose a tag to compare

@mtallo22 mtallo22 released this 09 Sep 21:49

WordPress toolbar drop-down menus opened behind the warning banner.

What was wrong

The banner sat at the very top of the stacking order — z-index: 2147483640, the maximum a browser accepts. WordPress puts #wpadminbar at 99999, and the toolbar's drop-down menus are children of the toolbar, so they share that stacking context.

The banner therefore outranked every toolbar menu. The bar itself was clear of the toolbar after 4.20.2, but the moment a menu dropped past it, that menu was painted underneath the banner and could not be clicked.

What changed

All three banners now sit just below the toolbar:

Banner z-index
Staging index 99990
Dev URLs 99989
robots.txt 99988

High enough to clear ordinary page content, low enough that the toolbar and everything it owns always wins.

The same fault would have covered wp-admin's own modal windows, which sit at 160000 — the media library picker among them. Fixed by the same change.

Testing

The render test now asserts the stacking order directly: the banner has a z-index, it is below 99999 so toolbar menus stay on top, and it is above ordinary page content. 128 assertions across four suites plus 19 environment scenarios, all passing; 35 files lint clean.

v4.20.3: Single-pass scan, and filenames are not hostnames

Choose a tag to compare

@mtallo22 mtallo22 released this 09 Sep 19:04

Two faults, found together on a live site whose only matches were four uploaded screenshots.

A filename is not a hostname

screencapture-2-hoziodev-2024-11-13.jpg reads as a hostname — labels of letters, digits and hyphens, ending in .jpg. The repair treated it as a link and would have replaced the filename with the live domain, orphaning the image.

A build pattern must now be a whole label of the name, which is what a real build host always is (18.hoziodev.com, client.mystagingwebsite.com). A pattern buried inside a single label, as it is in a filename, is no longer a hostname.

This was caught by the regression test written for the report, not by inspection.

The count said work was needed when none was

Every scan made three passes over each table — a COUNT, a 200-row sample for hostnames, then a full evaluation walk. On a large site that overran the time budget, and an unfinished pass fell back to assuming every row it had found was fixable.

The visible result was a panel that contradicted itself:

4 database rows still point at a dev domain
For information only — 0 rows mention a dev domain but are not links…
(followed by four listed rows)

…with a Fix button that ran for a long time and changed nothing, because none of the four could be rewritten.

The scan is now a single pass. It counts each row, decides whether the repair could rewrite it, and collects hostnames — all in one walk. Roughly a third of the database work, and the counts are exact.

Related

  • An unfinished pass no longer invents work. It reports only what it examined, says plainly on the settings screen that it stopped early, and leaves the banner keyed to what is known rather than assumed.
  • The time budget adapts to the site's own PHP execution limit instead of a fixed 20 seconds, with headroom so a long pass never dies mid-request.
  • A row left alone because the dev name sits in an uploaded file's name now says exactly that, instead of the generic "not a web address".

Testing

125 assertions across four suites plus 19 environment scenarios, all passing; 35 files lint clean. The new fixture reproduces the reported site exactly — four wp_postmeta rows whose values are upload paths containing a dev name — and asserts none is counted as fixable, all four are listed with a filename-specific reason, no hostname is claimed from them, and the banner stays off.

v4.20.2: Banners no longer cover the WordPress toolbar

Choose a tag to compare

@mtallo22 mtallo22 released this 09 Sep 14:43

The red warning banner covered the WordPress toolbar on the front end.

What was wrong

All three banners — staging index, dev URLs and robots.txt — sat on top of the admin bar for every logged-in administrator, instead of below it.

The offset was written as a stylesheet rule:

.admin-bar #hozio-sig-bar { top: 32px }

…while the element itself carried top:0 in its own inline style attribute. An inline declaration beats any stylesheet rule that is not marked !important, so the offset could never apply and the bar was pinned to the top of the window. Present since the first banner shipped in 4.17.0.

What changed

Placement now lives entirely in the stylesheet — the inline style sets position:fixed and nothing else.

The offset is worked out in PHP from is_admin_bar_showing() rather than from the admin-bar body class, which a theme that forgets body_class() never adds. So:

  • Toolbar showing → the bar starts below it (32px, or 46px below 782px wide, matching WordPress' own bump).
  • An administrator who has switched the toolbar off in their profile → no offset at all, and no pointless media query.

Testing

A render test runs the real banner and inspects the output: the inline style sets no top, left or right; the stylesheet carries the correct offset in both toolbar states; and nothing anywhere in the output pins the bar to the top of the window while the toolbar is present. 13 assertions across the two states, plus the existing 105 assertions and 19 environment scenarios — all passing, 35 files lint clean.

v4.20.1: A live site is no longer detected as staging after a transfer

Choose a tag to compare

@mtallo22 mtallo22 released this 04 Sep 13:29

Fixes a live site being detected as staging after a staging-to-live transfer.

What was wrong

WP_ENVIRONMENT_TYPE — which Pressable sets per site — travels with a staging-to-live transfer, in both the files and the database. A genuinely live site therefore kept claiming to be staging, and the guard believed it over the site's own address.

That is the dangerous direction. A site treated as staging is offered a Disallow: / robots.txt and a "discourage search engines" switch — the two things a live site must never have. Seen on two live client sites, both of which were presented with a "Fix both now" button that would have deindexed them.

No damage occurred on either: both still serve a correct robots.txt and send index, follow.

What changed

The site address decides. Every build environment in this fleet is identifiable from its address, and an address cannot lie about itself. A live domain is production even when WP_ENVIRONMENT_TYPE disagrees.

The manual override still wins over everything, which is how a dev site on a custom domain is handled.

The settings screen now shows why. A new "Detected because" line names the address that matched, or the override in force — so a surprising verdict is never unexplained again. Where WP_ENVIRONMENT_TYPE contradicts a live address, the panel says so and points at wp-config.php, since the stale value is worth clearing at the source.

Also

Spacing under the Save Settings button, which sat too close to the Live robots.txt section below it.

Testing

Two new regression scenarios: a live domain whose WP_ENVIRONMENT_TYPE still says staging must be production and must flag the contradiction; a real staging address must stay staging regardless. 105 assertions across four suites plus 15 environment scenarios, all passing; 35 files lint clean.

v4.20.0: Live robots.txt Template with in-WordPress editor

Choose a tag to compare

@mtallo22 mtallo22 released this 01 Sep 20:03

Every live site should serve the standard Hozio robots.txt with the Sitemap line pointing at its own domain. The plugin now watches whether the served file matches, warns when it does not, and gives you an editor and a one-click "apply the standard" — all manual, nothing automatic.

Detection

  • The Sitemap domain is worked out from the Site Address, verbatim. A www site gets www, a bare-domain site does not — never guessed.
  • A served Sitemap in the wrong www form is called out by name, in both directions. One pointing at a different domain entirely (the file a clone carries in) is flagged as carried over.
  • Comparison ignores trivia. Comments, blank lines, line endings and directive case never raise a warning — only differences a crawler would act on count.
  • Zero new HTTP. Evaluation rides the daily fetch the Staging Index Guard already makes; page loads read one autoloaded option.

The banner

Red, admin-only, and it fixes nothing itself — it links to the anchored settings section (landing on the section, not the top of the page) plus a direct "View live file" link to domain.com/robots.txt. It stands down whenever the Staging Index Guard's blocked-site emergency banner is up, so two bars never argue. Staging sites are untouched — they keep their Disallow: / guard.

The editor

A new Live robots.txt section in Hozio Pro settings: view the file being served, edit it in a textarea, and save. Every write takes a backup first, verifies on disk, then proves itself over HTTP with cache-busted retries before claiming success.

Saving your own version makes that the expected content — the warning then compares against what you saved, so a deliberate customisation is never nagged for differing from the template. Apply standard template writes the standard file for this domain and returns to comparing against it.

Also

The dev-URL banner and admin-bar links now land on their section of the settings page instead of the top.

Testing

56 assertions across both www modes for the new guard — template generation, normalisation, all four Sitemap-host verdicts, custom-expected behaviour, and every evaluate() verdict including standing down for a blocked site — plus the existing 94 assertions across the other guards, all passing. 35 files lint clean on PHP 8.2.

v4.19.5: Dev URL count reaches zero on its own

Choose a tag to compare

@mtallo22 mtallo22 released this 01 Sep 16:16

The Dev URL Guard's number is now rows that can be fixed — and it reaches zero on its own.

What changed

Rows that merely mention a dev domain are not links. Two kinds turn up on a live site:

  • Not a web address. The word hoziodev or mystagingwebsite appears as a key name or a label, not as a hostname. There is nothing to rewrite.
  • Unreadable stored data. A PHP-serialised object from a plugin that is no longer installed. Rewriting it blind would corrupt it, and nothing reads it anyway.

Those rows are no longer counted. They do not turn the banner on, and nobody has to dismiss them by hand. Once every genuine link has been rewritten, the count is zero.

They are still listed on the settings screen, for information only — which table and row, what the row is (the option name, or the meta key and post), a plain-English reason it was left alone, and the text it holds. So the count can read zero while the list still explains exactly why a few rows were skipped.

Removed

The manual "Ignore this row" list from 4.19.4. Nothing should require a human to click past it; the guard now works out for itself what is and is not a link.

Also

  • Hozio Pro's own Hub state and update-history options are excluded from the scan alongside the guard's own bookkeeping. They are records, not links.
  • Scan and Fix messages report rows needing a fix, and say separately how many rows are listed for information.

Testing

77 assertions across the two Dev URL Guard suites plus the 17 staging-guard scenarios, all passing. New coverage: with only a non-hostname mention remaining, the fixable count is zero and the banner flag is off; the leftover is listed with its table, row, label and reason; and before fixing, the count excludes the non-hostname row while the banner flag stays on for the rows that genuinely need work.

v4.19.4: Dev URL Guard can reach zero honestly

Choose a tag to compare

@mtallo22 mtallo22 released this 01 Sep 15:54

The Dev URL Guard can now reach zero honestly.

Why "3 rows" was never going to hit 0 on its own

After 4.19.3 rewrote 713 rows in one pass, a live site sat at 3 leftovers in wp_options — and those three listed five different build hostnames between them (master, master8, 6, 24, and a staging host). One row holding a list of several sites is the signature of a fleet or lineage record, not a link. Rows like that are supposed to contain old hostnames: a log, a history, a record of where the site was cloned from. Rewriting them would falsify them, and counting them for ever meant the guard could never report a clean site.

What is new

Ignore this row. Every leftover row now carries a link to dismiss it. Ignored rows drop out of the count and the repair, the panel shows how many are ignored, and one link clears the list. That is the honest path to zero for rows that should keep their history.

Every leftover is identified, not just counted. Table, row id, and what the row is — the option name, or the meta key and the post it belongs to — alongside the text it contains and the reason it was left alone. Both kinds are shown: rows that mention a dev domain without holding a rewritable hostname, and rows whose stored data cannot be safely rewritten. The second kind was previously counted and never displayed at all.

Fixes found on the way

  • The repair refused to run when the scan had not identified a hostname. That hid exactly the rows an owner needs to see once only non-hostname leftovers remain. Rows discover their own hostnames now, so the bail-out was unnecessary and harmful; it is gone.
  • Removed a dependency on the mbstring PHP extension in the new row labels. It is not guaranteed on every host and would have produced a fatal error on the settings screen where absent. Caught by running the suite under php -n.

Testing

73 assertions across the two Dev URL Guard suites plus the 17 staging-guard scenarios, all passing. New coverage: ignoring a row removes it from both the scan count and the repair, clearing the list brings it back, and every leftover carries a table, row id and human label.