Skip to content

Authenticatie & Rollen

De volledige authenticatie wordt afgehandeld via een template_redirect hook in functions/login.php.

Bezoeker request
|
template_redirect hook
|
is_admin()? ---- Ja ----> Doorlaten (wp-admin eigen auth)
|
wp-login.php? -- Ja ----> Doorlaten (veiligheidsnet)
|
AJAX/REST? ----- Ja ----> Doorlaten
|
Ingelogd?
|--- Ja --> Is /login/? -- Ja --> Redirect naar /
| -- Nee --> Toon pagina
|
|--- Nee -> Is /login/? -- Ja --> Toon login pagina
-- Nee --> Redirect naar /login/
add_action('template_redirect', function () {
if ( is_admin() ) return;
$login_slug = 'login';
// Sta wp-login.php toe (veiligheidsnet)
if ( strpos($_SERVER['REQUEST_URI'] ?? '', 'wp-login.php') !== false ) return;
// Sta AJAX/REST toe
if ( (defined('DOING_AJAX') && DOING_AJAX)
|| (defined('REST_REQUEST') && REST_REQUEST) ) return;
// Wel ingelogd: /login -> /
if ( is_user_logged_in() ) {
if ( is_page($login_slug) ) {
wp_safe_redirect( home_url('/') , 302 );
exit;
}
return; // verder alles toegestaan
}
// Niet ingelogd: alleen /login toegestaan
if ( is_page($login_slug) ) return;
wp_safe_redirect( home_url('/' . $login_slug . '/') , 302 );
exit;
});
Type requestCheckReden
Admin requests (/wp-admin/)is_admin()WordPress eigen login-check handelt dit af
wp-login.phpstrpos(REQUEST_URI)Veiligheidsnet voor directe wp-login toegang
AJAX requestsDOING_AJAX constantAJAX endpoints moeten altijd bereikbaar zijn
REST API requestsREST_REQUEST constant/wp-json/ endpoints moeten functioneren
  • REST API: Wordt expliciet doorgelaten via REST_REQUEST constant. REST endpoints hebben hun eigen authenticatie via rest_authentication_errors.
  • AJAX: Wordt doorgelaten via DOING_AJAX. Elke AJAX handler doet zijn eigen nonce + capability check.
  • WP Cron: Wordt niet expliciet afgevangen, maar WP Cron draait via wp-cron.php dat niet door template_redirect gaat (het laadt wp-load.php direct).
  • Monteur naar /wp-admin/: De is_admin() check laat dit door. WordPress zelf toont dan het dashboard. Monteurs (subscribers) zien een beperkt dashboard zonder menu-items die manage_options vereisen.
add_filter('logout_redirect', function ($redirect_to, $requested_redirect_to, $user) {
return home_url('/');
}, 10, 3);

Na uitloggen wordt altijd geredirect naar de homepage. Omdat de gebruiker dan niet meer is ingelogd, vangt template_redirect dit op en stuurt door naar /login/.

add_filter('auth_cookie_expiration', function ($expiration, $user_id, $remember) {
return 30 * DAY_IN_SECONDS; // 30 dagen
}, 10, 3);
  • Cookie duur: 30 dagen (2.592.000 seconden)
  • De $remember parameter wordt genegeerd — ook zonder “Onthoud mij” duurt de sessie 30 dagen
  • Risico op gedeelde apparaten — zie Security Audit

Slechts 2 rollen. Alle andere worden permanent verwijderd bij init (prioriteit 20).

add_action('init', function () {
// 1) Hernoem rollen in de UI
if ( wp_roles() ) {
$wp_roles = wp_roles();
if ( isset($wp_roles->roles['administrator']) ) {
$wp_roles->roles['administrator']['name'] = 'Beheerder';
$wp_roles->role_names['administrator'] = 'Beheerder';
}
if ( isset($wp_roles->roles['subscriber']) ) {
$wp_roles->roles['subscriber']['name'] = 'Monteur';
$wp_roles->role_names['subscriber'] = 'Monteur';
}
}
// 2) Verwijder standaard rollen
foreach (['editor', 'author', 'contributor'] as $role) {
if ( get_role($role) ) {
remove_role($role);
}
}
// 3) Geef Monteur (subscriber) delete capabilities
$role = get_role('subscriber');
if ( $role ) {
$role->add_cap('delete_posts');
$role->add_cap('delete_published_posts');
$role->add_cap('delete_private_posts');
$role->add_cap('delete_others_posts');
$role->add_cap('delete_pages');
$role->add_cap('delete_published_pages');
$role->add_cap('delete_private_pages');
$role->add_cap('delete_others_pages');
$role->add_cap('delete_attachments');
}
}, 20);
WP RolUI LabelBeschrijving
administratorBeheerderVolledige toegang
subscriberMonteurStandaard subscriber + uitgebreide delete rechten

Verwijderde rollen: editor, author, contributor — permanent via remove_role().

CapabilityBeheerderMonteur
readJaJa
edit_postsJaJa
delete_postsJaJa (toegevoegd)
delete_published_postsJaJa (toegevoegd)
delete_private_postsJaJa (toegevoegd)
delete_others_postsJaJa (toegevoegd)
delete_pagesJaJa (toegevoegd)
delete_published_pagesJaJa (toegevoegd)
delete_private_pagesJaJa (toegevoegd)
delete_others_pagesJaJa (toegevoegd)
delete_attachmentsJaJa (toegevoegd)
upload_filesJaJa
manage_optionsJaNee

Voorkomt dat andere rollen (ook door plugins aangemaakt) zichtbaar zijn in de user editor:

add_filter('editable_roles', function ($roles) {
$allowed = ['administrator', 'subscriber'];
foreach ($roles as $key => $role) {
if ( ! in_array($key, $allowed, true) ) {
unset($roles[$key]);
}
}
return $roles;
});

Forceert dat alleen administrator of subscriber als rol kan worden opgeslagen. Onbekende rollen worden teruggezet naar subscriber:

add_filter('pre_user_role', function ($new_role) {
$allowed = ['administrator', 'subscriber'];
return in_array($new_role, $allowed, true) ? $new_role : 'subscriber';
});
  • Veldnaam: admin (true/false) op user profiel
  • Vereist voor: melding verwijderen via beam_user_can_delete_jobs()
  • Dit is een eigen permissielaag, los van WordPress capabilities
  • Zie AJAX Endpoints voor de volledige beam_user_can_delete_jobs() implementatie
  • Veldnaam: employer (true/false) op user profiel
  • Markeert gebruiker als monteur/technician
  • acs_get_employers_all_index() bouwt een gecachte index (12 uur transient)

Verborgen voor alle gebruikers (inclusief administrators):

add_filter('show_admin_bar', '__return_false');

De CSS “bump” die WordPress toevoegt voor de admin bar wordt ook verwijderd:

add_action('get_header', 'my_filter_head');
function my_filter_head() {
remove_action('wp_head', '_admin_bar_bump_cb');
}

De login pagina is een WordPress Page met slug login. De styling wordt aangepast via hooks in functions/admin.php.

function my_custom_login_logo() {
echo '<style type="text/css">
h1 a {
background-image:url('.get_bloginfo('template_directory').'/assets/img/logo-login.png) !important;
background-size:auto !important;
height:100px !important;
width:300px !important;
}
</style>';
}
add_action('login_head', 'my_custom_login_logo');
  • Logo: /assets/img/logo-login.png (300x100px, centered)
  • Injectie via CSS in de login_head hook
add_filter('admin_footer_text', 'remove_footer_admin');
function remove_footer_admin() {
echo 'Made by <a href="http://www.angelovaudo.com" target="_blank">Angelo Vaudo</a>';
}
function admin_menu_fix() {
echo '<style>#adminmenu { transform: translateZ(0); }</style>';
}
add_action('admin_head', 'admin_menu_fix');

Voorkomt z-index rendering problemen in het WordPress admin menu.

add_action('init', 'register_menus');
function register_menus() {
register_nav_menus(
array(
'menu' => __( 'Main menu' ),
'mobile' => __( 'Mobile menu' )
)
);
}
LocationBeschrijving
menuDesktop horizontaal menu, gerenderd in partials/navigation.php
mobileMobiel menu, gerenderd als onderdeel van de account dropdown

Bestand: partials/navigation.php

De navigatie is alleen zichtbaar voor ingelogde gebruikers (is_user_logged_in()).

$current_user = wp_get_current_user();
$first_name = trim( (string) get_user_meta($current_user->ID, 'first_name', true) );
$user_name = $first_name;
if ( $user_name === '' ) {
$user_name = $current_user->display_name ?: $current_user->user_login;
}

Prioriteit: voornaam > display_name > user_login.

De profielfoto komt uit het ACF user-veld img-profile. Drie formaten worden ondersteund:

$avatar_url = '';
if ( function_exists('get_field') ) {
$acf_profile_img = get_field('img-profile', 'user_' . $user_id);
if ( is_array($acf_profile_img) ) {
// ACF image array: gebruik medium size, fallback naar full URL
$avatar_url = !empty($acf_profile_img['sizes']['medium'])
? $acf_profile_img['sizes']['medium']
: ($acf_profile_img['url'] ?? '');
} elseif ( is_numeric($acf_profile_img) ) {
// Attachment ID: haal medium src op
$src = wp_get_attachment_image_src((int) $acf_profile_img, 'medium');
$avatar_url = $src ? $src[0] : '';
} elseif ( is_string($acf_profile_img) ) {
// Directe URL string
$avatar_url = $acf_profile_img;
}
}
Return type van ACFAfhandeling
Array (image object)$img['sizes']['medium'] of $img['url']
Numeric (attachment ID)wp_get_attachment_image_src($id, 'medium')
String (URL)Directe URL
Geen/leegGeen profielfoto getoond

Er is geen Gravatar fallback. Als img-profile niet ingesteld is, wordt geen afbeelding getoond.

<nav class="site-nav" aria-label="Hoofdnavigatie">
<div class="container nav-inner">
<!-- Logo kolom -->
<div class="nav-column nav-column-logo">
<a href="/" class="site-logo">
<!-- SVG inline via file_get_contents() -->
</a>
</div>
<!-- Desktop menu -->
<div class="nav-column nav-column-menu show-desktop" id="primary-menu">
<!-- wp_nav_menu('menu') -->
</div>
<!-- Account dropdown -->
<div class="nav-column nav-column-account">
<div class="account-dropdown" data-account-dropdown>
<button class="account-trigger" aria-expanded="false" aria-controls="account-menu">
<img class="img-profile" src="..." /> <!-- conditioneel -->
<span class="account-name">Voornaam</span>
<span class="account-caret">&#9662;</span>
</button>
<ul class="account-menu" id="account-menu" role="menu">
<!-- Mobiel menu (hide-desktop) -->
<!-- wp_nav_menu('mobile') als <li> wrapper -->
<li>
<a class="logout" href="...">Uitloggen</a>
</li>
</ul>
</div>
</div>
</div>
</nav>
ElementDesktopMobiel
nav-column-menuZichtbaar (show-desktop)Verborgen
Mobile menu in dropdownVerborgen (hide-desktop)Zichtbaar als onderdeel van account dropdown
Account dropdownProfielfoto + naam + caretProfielfoto + naam + caret
UitlogknopAltijd in dropdownAltijd in dropdown

Het logo (/assets/img/logo-tina.svg) wordt inline gerenderd via file_get_contents(). Dit voorkomt een extra HTTP request en maakt CSS styling mogelijk.

$logout_url = wp_logout_url( home_url('/') );

De redirect na logout gaat naar /, maar het logout_redirect filter overschrijft dit ook naar home_url('/'). Vervolgens vangt template_redirect de niet-ingelogde gebruiker op en stuurt door naar /login/.

if( function_exists('acf_add_options_page') ) {
acf_add_options_page();
}

Standaard ACF options page zonder parameters. Beschikbaar onder Settings > Options in wp-admin.

BestandPadHooksFuncties
login.phpfunctions/login.phptemplate_redirect, logout_redirect, auth_cookie_expirationLogin redirect logica, sessie duur
roles.phpfunctions/roles.phpinit (prio 20), editable_roles, pre_user_roleRol setup, capability toewijzing, rol lockdown
admin.phpfunctions/admin.phpshow_admin_bar, login_head, admin_footer_text, get_header, admin_head, initAdmin bar, login styling, menu registratie, ACF options
navigation.phppartials/navigation.php(partial, geen hooks)Navigatie HTML met profielfoto, menu’s, account dropdown