Contents
Overview
This article explains, in full detail, how to limit the block editor (Gutenberg) to a specific set of blocks per user role using PHP. It covers the available filters, practical examples, per-post-type rules, wildcard matching, dynamic configuration, where to place the code (theme vs plugin), and troubleshooting tips. All code examples are ready to paste into a themes functions.php or into a small plugin file.
What you need to know first
- Goal: Allow only certain block types for certain user roles (for example: allow editors only paragraph and image blocks).
- Primary hooks:
- allowed_block_types_all — recommended when available: receives the current block whitelist and an editor context array (post_type, post, etc.).
- allowed_block_types — older filter that receives the allowed list and the post object (fallback if the _all filter is not available in your environment).
- Block names: Block identifiers are strings like core/paragraph, core/image, my-plugin/my-block. Use exact names when returning allowed lists, or compute names from the block registry.
- Where to add the code: Themes functions.php or create a small plugin (recommended for portability and persistence across themes).
Key implementation concepts
- Detect user role: Use wp_get_current_user() and check user->roles. Do not rely on display names — roles are stored in the user object.
- Be defensive: The incoming allowed list may be null or boolean true — detect that and produce an explicit array of strings (or return true to allow everything).
- Per-post-type filtering: Use the editor context (post_type) if available to limit blocks on a per-post-type basis (e.g., pages vs posts vs custom post types).
- Intersect with registered blocks: If you declare block names that are not registered, they will simply be ignored for robustness, compute intersection with WP_Block_Type_Registry to avoid typos.
- Wildcard/prefix: You can allow all blocks from a namespace (e.g., all core blocks core/) by matching prefixes against registered block names.
Simple example: allow only a few blocks for the Editor role
This example uses the modern filter allowed_block_types_all (preferred). For backward compatibility you may also register an allowed_block_types fallback (shown later).
lt?php / Limit allowed blocks for the editor role. Return an explicit array of block names for editors, otherwise return default. / function my_allowed_blocks_by_role( allowed_blocks, editor_context ) { // Get current user user = wp_get_current_user() if ( ! user empty( user->roles ) ) { return allowed_blocks } // If user is an editor, restrict blocks if ( in_array( editor, (array) user->roles, true ) ) { // Explicit allowed blocks for editors return array( core/paragraph, core/image, core/heading, core/list, ) } // For all other roles, do not change the allowed blocks return allowed_blocks } add_filter( allowed_block_types_all, my_allowed_blocks_by_role, 10, 2 ) ?gt
Notes
- If the editor_context is available you can check editor_context[post_type] to apply different rules for pages vs posts.
- Return an array of names. Returning true allows all registered blocks returning false prevents all blocks (not typically useful).
Per-role and per-post-type configuration (practical approach)
Use a configuration array mapping roles to allowed block lists, and optionally add post type rules. This scales better than multiple functions.
lt?php / Config-driven allowed blocks per role and per post type. / function config_allowed_blocks_by_role( allowed_blocks, editor_context ) { user = wp_get_current_user() if ( ! user ) { return allowed_blocks } // Example config: role => [ post_type => [blocks] ] or role => [blocks] for global per-role config = array( administrator => true, // true means allow all registered blocks editor => array( post => array( core/paragraph, core/image, core/heading ), page => array( core/paragraph, core/heading ), // pages less media ), author => array( core/paragraph ), // authors only get paragraph globally ) // Determine role (first matched role) role = null foreach ( (array) user->roles as r ) { if ( isset( config[ r ] ) ) { role = r break } } // If no special role config, leave defaults if ( ! role ) { return allowed_blocks } role_setting = config[ role ] // true means allow all if ( role_setting === true ) { return true } // If post type context is present and a mapping exists post_type = isset( editor_context[post_type] ) ? editor_context[post_type] : null if ( post_type is_array( role_setting ) isset( role_setting[ post_type ] ) ) { desired = role_setting[ post_type ] } else { // Fallback to global list for role (if config is direct array) desired = is_array( role_setting ) ? role_setting : allowed_blocks } // Ensure we only return names that are actually registered registered = array_keys( WP_Block_Type_Registry::get_instance()->get_all_registered() ) final_allowed = array_intersect( registered, desired ) return final_allowed } add_filter( allowed_block_types_all, config_allowed_blocks_by_role, 10, 2 ) ?gt
Why intersect with registered blocks?
To avoid typos or references to blocks that are not registered on the site. Returning a block name that does not exist is silently ignored by the editor, but producing a final list that only contains valid block names is cleaner and helps you detect configuration mistakes during development.
Fallback for older environments: allowed_block_types
Some environments or plugin stacks may not expose allowed_block_types_all. Use a fallback that supports the older signature. Note the older filter passes the post object rather than an editor context.
lt?php / Fallback using allowed_block_types signature. / function fallback_allowed_block_types( allowed_blocks, post ) { user = wp_get_current_user() if ( ! user ) { return allowed_blocks } if ( in_array( editor, (array) user->roles, true ) ) { // Example: editors allowed only paragraph and image on posts if ( post isset( post->post_type ) post->post_type === post ) { return array( core/paragraph, core/image ) } return array( core/paragraph ) } return allowed_blocks } add_filter( allowed_block_types, fallback_allowed_block_types, 10, 2 ) ?gt
Wildcard or namespace matching (allow all core/)
If you want to allow all blocks within a namespace (e.g., all core blocks) without listing each block individually, compute the allowed set by filtering the registered blocks for a prefix match.
lt?php / Allow all core/ blocks for a role (wildcard prefix match). / function allow_core_namespace_for_role( allowed_blocks, editor_context ) { user = wp_get_current_user() if ( ! user ) { return allowed_blocks } if ( in_array( contributor, (array) user->roles, true ) ) { all_registered = WP_Block_Type_Registry::get_instance()->get_all_registered() names = array_keys( all_registered ) core_allowed = array_filter( names, function( name ) { return strpos( name, core/ ) === 0 } ) // array_values to reindex if desired return array_values( core_allowed ) } return allowed_blocks } add_filter( allowed_block_types_all, allow_core_namespace_for_role, 10, 2 ) ?gt
Common extra examples
- Allow a custom block only for editors: Use the block name my-plugin/my-block in the allowed array.
- Allow different sets for multiple roles: Use a mapping array (role => blocks) as shown earlier.
- Allow all blocks for administrators: Return true for administrators so they are not restricted.
Where to place this code
- Theme: functions.php — quick and simple, but role restrictions will disappear if you switch themes.
- Plugin: Create a small plugin and put the code in the main plugin file. This is recommended for portability and upgrades.
Example plugin header to create a small plugin file (place this at top of file in wp-content/plugins/my-block-limiter/my-block-limiter.php):
lt?php / Plugin Name: Block Limiter by Role Description: Limit allowed block types in the editor per user role. Version: 1.0 Author: Your Name / // (Add the filter function code here) ?gt
Testing and debugging tips
- Log available registered block names to debug what to allow:
lt?php registered = WP_Block_Type_Registry::get_instance()->get_all_registered() error_log( Registered blocks: . implode( , , array_keys( registered ) ) ) ?gt
- Test with users assigned the target roles. Create test accounts for each role.
- Clear caches (object cache and any admin caching plugins). Some hosts aggressively cache admin output.
- When testing on multisite, ensure network activation rules and per-site block registration do not change available block types unexpectedly.
- If the editor still shows blocks you expected to hide, ensure no other plugin re-adds them, and check filter priority conflicts — set a later or earlier priority as required.
Troubleshooting common problems
- Block not disappearing: Confirm the block name is correct. Use registered names from the block registry rather than guessing.
- Filter not firing: Ensure the filter is added at plugin load time (no conditional that stops it), and check for typos in the hook name.
- Role check failing: Remember a user can have multiple roles. Use in_array on user->roles as shown.
- Third-party blocks still visible: They may be registered after your code runs in rare setups. You can increase priority or call your filter at a later stage, but allowed_block_types_all is normally applied at the right moment for the editor.
Security and best practices
- Do not rely on client-side enforcement only. The allowed_block_types filters change the editor configuration in PHP which is the authoritative place to limit blocks. There are no security implications beyond correct capability checks unless you use custom server-side rendering that depends on block content — in those cases validate block content when saving.
- Prefer returning explicit arrays rather than lightweight client-side JS hacks PHP-side filters will be applied consistently.
- Document your configuration array, and keep block names in a single place within your plugin or theme to avoid duplicated magic strings.
Advanced: dynamic administration UI (quick outline)
If you want a UI to configure which roles can use which blocks, simple steps:
- Build an options page with roles and a multi-select of registered blocks (use WP_Block_Type_Registry to list blocks).
- Save the mapping into an option (e.g., my_block_limiter_settings).
- Use the saved mapping inside your allowed_block_types_all filter to return the selected blocks per role.
Compatibility notes
- Both the allowed_block_types and allowed_block_types_all filters operate on server-side configuration used by the block editor. Use allowed_block_types_all when available for better context (post_type, post), otherwise fallback to allowed_block_types.
- Custom block editors or specialized plugin editors may behave differently test with the exact editor configuration used on your site.
Useful references
Complete sample plugin (compact)
A compact, self-contained plugin that enforces different block sets for administrators, editors and authors:
lt?php / Plugin Name: Compact Block Limiter Description: Example: limit blocks per role (admin: all, editor: paragraph image, author: paragraph). Version: 1.0 Author: Example / function compact_block_limiter( allowed_blocks, editor_context ) { user = wp_get_current_user() if ( ! user ) { return allowed_blocks } // Admins get everything if ( in_array( administrator, (array) user->roles, true ) ) { return true } // Editors restricted on posts if ( in_array( editor, (array) user->roles, true ) ) { // You can use post_type context if needed if ( isset( editor_context[post_type] ) editor_context[post_type] === post ) { return array( core/paragraph, core/image ) } return array( core/paragraph, core/heading ) } // Authors get only paragraphs if ( in_array( author, (array) user->roles, true ) ) { return array( core/paragraph ) } return allowed_blocks } add_filter( allowed_block_types_all, compact_block_limiter, 10, 2 ) ?gt
Final checklist before deploying
- Test with at least one test user per role.
- Confirm block names are correct and available on the site.
- Decide whether the change should be theme-scoped (functions.php) or site-scoped (plugin).
- Document behavior for site editors and train them on the allowed blocks.
- Consider an option to temporarily disable the restrictions for troubleshooting.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |