Third Party Scripts
Many WordPress sites require external tracking, analytics, or functionality scripts. This page explains how to add third-party scripts to LumberMill.
ACF Option Pages (recommended)
Section titled “ACF Option Pages (recommended)”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 field | Purpose |
|---|---|
title | Name to identify the script in the admin |
position | Where the script is output |
active | Toggle the script off without deleting it |
code | The full script snippet |
Position values map to WordPress actions:
| Position | Output location |
|---|---|
head | <head> via wp_head |
body_open | After <body> via wp_body_open |
body_close | Before </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.
Script tags and DISALLOW_UNFILTERED_HTML
Section titled “Script tags and DISALLOW_UNFILTERED_HTML”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.
Locking the field to one user
Section titled “Locking the field to one user”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=1config/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.
Custom Hook
Section titled “Custom Hook”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>tagwp_footer— insert before closing</body>
Dynamic values
Section titled “Dynamic values”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
Section titled “External Scripts”External scripts can be registered via the config/assets.php file.

