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);
  • Olawale

    Olawale Adesina is a coder who lives in Ilorin, Nigeria. He has been working with PHP for nearly a decade, and has developed for WordPress since 2015.

2 Comments

  1. 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);

Leave a Reply

Your email address will not be published. Required fields are marked *