Skip to content

Third Party Scripts

Many WordPress sites require external tracking, analytics, or functionality scripts. This page explains how to add third-party scripts to LumberMill.

LumberMill includes a built-in ThirdPartyScriptsHook that reads script snippets from the Scripts repeater (theme_settings_third_party_scripts) on the Theme Settings options page and outputs them at the correct positions. This lets content editors manage scripts without touching code.

Each row has these sub fields:

Sub fieldPurpose
titleName to identify the script in the admin
positionWhere the script is output
activeToggle the script off without deleting it
codeThe full script snippet

Position values map to WordPress actions:

PositionOutput location
head<head> via wp_head
body_openAfter <body> via wp_body_open
body_closeBefore </body> via wp_footer

Paste the full script tag (e.g. Google Tag Manager, Meta Pixel) into the Code field, pick a position, and it will be output automatically. Rows output in repeater order per position.

LumberMill sets DISALLOW_UNFILTERED_HTML to true, which removes the unfiltered_html capability from every user. ACF reacts to that by running wp_kses_post_deep() over the whole submission on save, and wp_kses_post() strips <script> tags.

ThirdPartyScriptsHook works around this for the Code sub field only: it copies the submitted values on admin_init, then writes them back on acf/save_post (priority 1, before ACF stores anything). Every other field, on this page and everywhere else, stays sanitized.

Because raw <script> is the one thing that survives DISALLOW_UNFILTERED_HTML, the field is restricted to a single account. Set the user ID in .env:

THIRD_PARTY_SCRIPTS_USER=1

config/application.php turns that into the THIRD_PARTY_SCRIPTS_USER constant. The user must also have manage_options.

When the constant is empty, or the current user is anyone else:

  • the Scripts repeater is hidden from the Theme Settings page (acf/prepare_field)
  • a submission that still carries the field is dropped, so the stored rows are left untouched
  • no raw code is restored, so anything that did get through is sanitized by ACF as usual

Scripts already saved keep rendering on the front end regardless of who is logged in.

For scripts that need to be added in code, create a method in a hook class and attach it to the appropriate WordPress action.

public function register(): void
{
add_action('wp_head', [$this, 'addGtm']);
}
public function addGtm(): void
{
echo '<script>/* GTM snippet */</script>';
}

Available actions:

  • wp_head — insert in <head>
  • wp_body_open — insert after <body> tag
  • wp_footer — insert before closing </body>
public function addInlineScript(): void
{
$value = get_some_option();
printf('<script>var myVar = "%s";</script>', esc_js($value));
}

Always escape dynamic values with esc_js() for JavaScript strings.

External scripts can be registered via the config/assets.php file.