How to Use unfiltered_html on WordPress Multisite Networks
WordPress multisite networks don't always follow the same rules as regular WordPress sites. User roles and capabilities are very different on multisite networks.
One significant difference is the behavior of the unfiltered_html capability. This is a security feature in WordPress that prevents users from using tags such as <iframe>
and <embed>
, plus also more advanced code such as Javascript.
On a Multisite network only users in the Super Admin role have this unfiltered_html capability. This can create a lot of issues for users who need to add or edit any content with iframes or embeds.
If you're a developer, this happens because WordPress has a global override for the method map_meta_cap()
in the wp-includes/capabilities.php
file. This ignores the unfiltered_html
capability in a multisite install if the user is not a Super Admin.
The code is in Line 425 of the wp-includes/capabilities.php
file.
Here are two ways you can solve the problem. Both of these are code snippets that you can add to your theme's functions.php file:
// Remove KSES if user has unfiltered_html cap
function umc_custom_kses_init() {
if ( current_user_can( 'unfiltered_html' ) ) {
kses_remove_filters();
}
}
add_action( 'init', 'umc_custom_kses_init', 11 );
add_action( 'set_current_user', 'umc_custom_kses_init', 11 );
This code snippet will also work:
function multisite_restore_unfiltered_html($caps, $cap, $user_id, $args ) {
if ( 'unfiltered_html' === $cap && user_can( $user_id, 'unfiltered_html' ) ) {
$caps = array( 'unfiltered_html' );
}
return $caps;
}
add_filter( 'map_meta_cap', 'multisite_restore_unfiltered_html', 1, 4 );
You could also try with this code:
/**
* Grant unfiltered_html capability to all users in WordPress multisite
*/
function grant_unfiltered_html_to_all_users() {
// Get all roles
global $wp_roles;
if (!isset($wp_roles)) {
$wp_roles = new WP_Roles();
}
// Loop through each role and add the unfiltered_html capability
foreach ($wp_roles->role_objects as $role) {
$role->add_cap('unfiltered_html');
}
}
// Hook into the init action to ensure it runs on every page load
add_action('init', 'grant_unfiltered_html_to_all_users');
/**
* Alternative approach using user-specific capability filter
* This will add the capability for all users regardless of their role
*/
function add_unfiltered_html_cap($caps, $cap, $user_id) {
// If the capability being checked is unfiltered_html
if ($cap === 'unfiltered_html') {
// Grant the capability by returning an empty array
return array();
}
// Return original capabilities for other capability checks
return $caps;
}
// Hook into the user capability filter
add_filter('user_has_cap', function($allcaps, $caps, $args, $user) {
// Add unfiltered_html capability
$allcaps['unfiltered_html'] = true;
return $allcaps;
}, 10, 4);
The second function falls victim to recursion by calling “user_can(‘unfiltered_html’);” – which will call “map_meta_cap” and therefore the same filter. Here’s a workaround:
function rpm_multisite_restore_unfiltered_html($caps, $cap, $user_id, $args){
// Prevent recursion by not using “user_can” type functions, which will call this filter
$user_meta = get_userdata($user_id);
$user_caps = $user_meta->allcaps;
if ($cap === “unfiltered_html” && !empty($user_caps[‘unfiltered_html’])){
$caps = [‘unfiltered_html’];
}
return $caps;
}
add_filter(‘map_meta_cap’, ‘rpm_multisite_restore_unfiltered_html’, 1, 4);
Thanks for sharing that, Dave