This article covers the technical implementation of nine WordPress modifications built for acteursbelangen.nl. Each task is documented with the specific approach used, the code that was written, and any non-obvious decisions made along the way.
Task 1: YouTube Thumbnail Fix
Actor profile pages on the site displayed video thumbnails using a lazy loader that read URLs from a data-bg attribute. The URLs were being generated with YouTube share parameters attached — for example, https://img.youtube.com/vi/4FM_loh9Y0w?si=ADwjtrwjmQgLSZau/mqdefault.jpg — which produced 404 errors because the ?si= parameter broke the path structure before /mqdefault.jpg.
The fix was a small JavaScript snippet added via WPCode to the footer. It runs on DOMContentLoaded, before the lazy loader processes the elements, and rewrites any malformed data-bg values to the correct format:
document.addEventListener("DOMContentLoaded", function () {
const videoThumbnails = document.querySelectorAll(".video_box_img_container");
videoThumbnails.forEach(function (el) {
let originalBg = el.getAttribute("data-bg");
if (originalBg && originalBg.includes("youtube.com/vi/")) {
// Extract clean video ID, strip any query string parameters
const match = originalBg.match(/youtube.com/vi/([a-zA-Z0-9_-]+)/);
if (match && match[1]) {
const videoId = match[1];
const cleanUrl = "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg";
el.setAttribute("data-bg", cleanUrl);
el.style.backgroundImage = "url(" + cleanUrl + ")";
}
}
});
});Task 2: Mobile Menu for Logged-In Actors
The mobile navigation slot (mobile_nav_2025) was showing the general site menu for all users, including logged-in actors who should see a profile-specific menu with links to their profile, photos, videos, CV, and logout. The correct menu was already built in the admin (menu ID 2, named Profiel menu) — it just was not being displayed for the right users.
A wp_nav_menu_args filter intercepts the menu arguments before output. When the current user is logged in with the subscriber role (which is the role used for actor members on this site), the filter replaces the default menu assignment with menu ID 2:
add_filter("wp_nav_menu_args", "replace_mobile_menu_for_logged_in_actors");
function replace_mobile_menu_for_logged_in_actors($args) {
if ($args["theme_location"] === "mobile_nav_2025") {
$user = wp_get_current_user();
if (is_user_logged_in() && in_array("subscriber", (array) $user->roles)) {
$args["menu"] = 2;
}
}
return $args;
}Task 3 and 6: Casting Director Role and Custom Fields
The Casting Director role was registered with a targeted set of capabilities — enough to create and manage their own tom_project posts, but without access to the broader admin panel used by editors and administrators:
add_role("casting_director", "Casting Director", [
"read" => true,
"publish_posts" => false,
"edit_posts" => true,
"edit_published_posts" => true,
"delete_posts" => false,
"upload_files" => true,
"edit_tom_projects" => true,
"publish_tom_projects" => true,
]);An ACF field group was registered for the casting_director user role, covering: company name, full name, address (street, postal code, city, country), email, phone, website, Instagram, affiliations (NCA, VVTP, NAPA, NCP as checkboxes), and an approval flag. These fields are stored against the user object using ACF’s user_{$user_id} key format.
Task 4 and 7: Frontend Registration and Admin Approval
The registration page at /aanmelden-casting-director used a custom HTML form handled by a WordPress action hook on init. On submission, the handler creates a new user with the casting_director role and immediately sets account_approved to false in user meta. A confirmation message is shown to the registrant explaining that their account is pending review.
The approval gate uses the wp_authenticate_user hook, which fires after credentials are verified but before the login session is created. If the user’s account_approved meta value is not true, the hook returns a WP_Error that blocks the login and shows an appropriate message:
add_filter("wp_authenticate_user", "block_unapproved_casting_directors", 10, 2);
function block_unapproved_casting_directors($user, $password) {
if (is_wp_error($user)) return $user;
if (in_array("casting_director", (array) $user->roles)) {
$approved = get_user_meta($user->ID, "account_approved", true);
if ($approved !== "true" && $approved !== true) {
return new WP_Error(
"account_pending",
"Your account is pending approval. You will be notified when access is granted."
);
}
}
return $user;
}Admin approval is done manually: the admin opens the user’s profile in wp-admin/user-edit.php, scrolls to the Casting Director meta section, and sets the Goedgekeurd (Approved) ACF field to true. On next login attempt the user passes through.
Task 8 and 9: Casting Management Interface
Casting Directors needed to create tom_project posts and see both their own castings and the actors who had applied. The capability assignment in the role registration handled the creation side. For the overview, a shortcode renders a filtered list of tom_project posts authored by the currently logged-in Casting Director, with each casting expandable to show its list of applicants stored in post meta.
Debugging the Actor Avatar in the Mobile Header
One issue that came up during implementation was an actor profile photo not appearing in the mobile navigation header. The image was stored correctly in ACF (confirmed by a debug snippet that rendered the URL and a preview image in the page footer), but was not displaying in the menu area. The root cause was that the avatar was being passed to a JavaScript variable (userImg) which was then expected to set a background-image on a DOM element — but the element selector in the existing script did not match the updated HTML structure. Adding an explicit injection for the correct element resolved it.
Admin Meta Viewer
To make all ACF and custom field values inspectable during development without installing a debug plugin, a small snippet was added via WPCode that appends a read-only table of all user meta key/value pairs to the bottom of wp-admin/user-edit.php, visible only to administrators. This was useful throughout the project for verifying that form submissions were storing data under the expected keys and that ACF field names matched what the template code was reading.
Summary
| Task | Solution |
|---|---|
| YouTube thumbnail 404 | JS snippet strips ?si= from data-bg before lazy load runs |
| Mobile menu for actors | wp_nav_menu_args filter swaps menu for subscribers |
| Casting Director role | Custom role registered with targeted capabilities |
| Frontend registration form | Custom HTML form + init handler, stores to ACF user fields |
| Admin approval gate | wp_authenticate_user hook checks account_approved meta |
| CTA buttons on Top of Mind | Shortcode or direct HTML insertion in page template |
| ACF fields for CD profile | ACF field group scoped to casting_director role |
| Create and manage castings | tom_project capabilities on role + frontend shortcode |
| Admin meta debug view | WPCode snippet appends all user meta to user-edit.php |
This article is part of a case study series:
WordPress Custom Role System for a Dutch Actor Platform
Read the full case study →