• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Step-by-Step Guide to building a custom role-based access control editor block for Gutenberg using PHP block-render callbacks

Step-by-Step Guide to building a custom role-based access control editor block for Gutenberg using PHP block-render callbacks

Leveraging PHP Block-Render Callbacks for Custom RBAC in Gutenberg

When building complex WordPress applications, granular control over content visibility based on user roles is a common requirement. While WordPress offers built-in roles, dynamically rendering specific block content only to authorized users necessitates a custom solution. This guide details how to create a custom Gutenberg block that utilizes PHP block-render callbacks to implement role-based access control (RBAC) directly within the editor and on the front-end.

Registering the Custom Block

We’ll start by registering a custom block type. This involves defining the block’s attributes, its editor interface (using JavaScript, which we’ll abstract for this PHP-focused example), and crucially, its server-side rendering logic via a PHP callback.

Create a PHP file within your theme’s `inc` directory or a custom plugin. For this example, let’s assume a plugin structure.

`custom-rbac-block.php`

<?php
/**
 * Plugin Name: Custom RBAC Block
 * Description: Adds a custom block for role-based access control.
 * Version: 1.0.0
 * Author: Your Name
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

/**
 * Registers the custom block type.
 */
function custom_rbac_block_register() {
    register_block_type( 'custom-rbac/role-restricted', array(
        'attributes' => array(
            'allowed_roles' => array(
                'type' => 'string',
                'default' => '', // Comma-separated list of roles.
            ),
            'restricted_content' => array(
                'type' => 'string',
                'default' => '', // The content to be restricted.
            ),
        ),
        'render_callback' => 'custom_rbac_block_render_callback',
    ) );
}
add_action( 'init', 'custom_rbac_block_register' );

/**
 * Server-side rendering callback for the custom RBAC block.
 *
 * @param array $attributes The block attributes.
 * @return string The HTML to render.
 */
function custom_rbac_block_render_callback( $attributes ) {
    $allowed_roles_string = isset( $attributes['allowed_roles'] ) ? $attributes['allowed_roles'] : '';
    $restricted_content   = isset( $attributes['restricted_content'] ) ? $attributes['restricted_content'] : '';

    if ( empty( $allowed_roles_string ) || empty( $restricted_content ) ) {
        return ''; // Nothing to render if roles or content are missing.
    }

    $allowed_roles = array_map( 'trim', explode( ',', $allowed_roles_string ) );
    $current_user  = wp_get_current_user();
    $user_roles    = $current_user->roles;

    // Check if the current user has any of the allowed roles.
    $has_access = false;
    foreach ( $allowed_roles as $role ) {
        if ( in_array( $role, $user_roles, true ) ) {
            $has_access = true;
            break;
        }
    }

    if ( $has_access ) {
        // Render the content if the user has access.
        // We'll assume restricted_content is already safe HTML or will be escaped.
        // For complex content, consider using do_blocks() if it's serialized block content.
        return $restricted_content;
    } else {
        // Optionally render a message for users without access.
        // return '<p>You do not have permission to view this content.</p>';
        return ''; // Or simply return nothing.
    }
}

In this PHP file:

  • We define the block using register_block_type.
  • The attributes array defines the data the block will store: allowed_roles (a comma-separated string of role slugs) and restricted_content (the HTML content to be shown).
  • The render_callback is set to custom_rbac_block_render_callback, which will handle the server-side rendering.
  • The custom_rbac_block_render_callback function retrieves the attributes, checks the current user’s roles against the allowed roles, and returns the restricted_content only if the user has sufficient permissions.

Defining the Editor Interface (JavaScript)

While the core logic is in PHP, Gutenberg blocks require a JavaScript component to define their appearance and controls in the editor. For a production-ready block, you would typically use @wordpress/scripts and @wordpress/blocks to build this. For brevity and focus on the PHP callback, we’ll outline the conceptual JavaScript structure.

`src/index.js` (Conceptual)

import { registerBlockType } from '@wordpress/blocks';
import { TextControl, TextareaControl, PanelBody } from '@wordpress/components';
import { InspectorControls, RichText } from '@wordpress/block-editor';

registerBlockType( 'custom-rbac/role-restricted', {
    title: 'Role Restricted Content',
    icon: 'lock',
    category: 'common', // Or a custom category

    edit: ( { attributes, setAttributes } ) => {
        const { allowed_roles, restricted_content } = attributes;

        const onRolesChange = ( value ) => {
            setAttributes( { allowed_roles: value } );
        };

        const onContentChange = ( value ) => {
            setAttributes( { restricted_content: value } );
        };

        return (
            <>
                <InspectorControls>
                    <PanelBody title="Access Control" initialOpen={ true }>
                        <TextControl
                            label="Allowed Roles (comma-separated)"
                            value={ allowed_roles }
                            onChange={ onRolesChange }
                            help="Enter WordPress role slugs (e.g., administrator, editor, subscriber)."
                        />
                    </PanelBody>
                </InspectorControls>
                <div className="role-restricted-block-editor">
                    <h3>Role Restricted Content (Editor View)</h3>
                    <p>Content below will only be visible to users with roles: { allowed_roles || 'None specified' }</p>
                    <RichText
                        tagName="div"
                        className="restricted-content-editor"
                        placeholder="Enter content to restrict..."
                        value={ restricted_content }
                        onChange={ onContentChange }
                        allowedFormats={ [ 'core/bold', 'core/italic', 'core/link' ] } // Example formats
                    />
                </div>
            </>
        );
    },

    save: () => {
        // The save function should return null because rendering is handled by PHP.
        // This is crucial for dynamic blocks.
        return null;
    },
} );

Key points for the JavaScript:

  • registerBlockType is used to define the block.
  • attributes in JavaScript must match those defined in PHP.
  • InspectorControls provides a sidebar panel for users to input the comma-separated allowed roles.
  • RichText allows users to input and format the content that will be restricted.
  • The save function returns null. This tells Gutenberg that the block’s content is entirely dynamic and will be rendered by the server-side callback.

Building and Enqueuing the JavaScript

To compile the JavaScript, you’ll need a build process. A common setup involves Node.js, npm/yarn, and the @wordpress/scripts package.

`package.json` (Example)

{
  "name": "custom-rbac-block",
  "version": "1.0.0",
  "description": "Custom RBAC Block for Gutenberg",
  "main": "build/index.js",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },
  "keywords": [
    "wordpress",
    "gutenberg",
    "block"
  ],
  "author": "Your Name",
  "license": "GPL-2.0-or-later",
  "devDependencies": {
    "@wordpress/block-editor": "^12.0.0",
    "@wordpress/blocks": "^12.0.0",
    "@wordpress/components": "^25.0.0",
    "@wordpress/scripts": "^26.0.0"
  }
}

Run npm install to install dependencies, then npm run build to compile the JavaScript into the build/index.js file. You’ll then need to enqueue this script in your plugin.

Enqueueing the Script in `custom-rbac-block.php`


The enqueue_block_editor_assets action hook ensures the script is loaded only within the Gutenberg editor. The dependencies array includes core WordPress packages required for block development.

Testing and Verification

After setting up the plugin and building the JavaScript:

  • Activate the “Custom RBAC Block” plugin.
  • Create or edit a post/page.
  • Add the “Role Restricted Content” block from the block inserter.
  • In the block settings sidebar, enter role slugs (e.g., administrator,editor).
  • Add some content within the block’s editor area.
  • Save the post/page.
  • View the post/page as different users (e.g., an administrator, an editor, a subscriber, or a logged-out user).

You should observe that the content is only displayed for users who possess at least one of the specified roles. For users without the required roles, the block will render nothing (or the alternative message if you uncommented that line in the PHP callback).

Advanced Considerations and Enhancements

This basic implementation can be extended significantly:

  • Dynamic Role Fetching: Instead of a text input, use a JavaScript component that fetches available WordPress roles dynamically, providing a dropdown or multi-select for easier role selection. This would involve a REST API endpoint or server-side rendering of role options.
  • Content Serialization: If the restricted_content needs to contain other Gutenberg blocks, you would store the serialized block data (JSON) and use do_blocks( $serialized_blocks ) within the PHP render callback to render them.
  • User Capabilities: For more fine-grained control, check user capabilities instead of just roles using current_user_can().
  • Frontend Scripting: For certain UI elements within the restricted content that might require JavaScript interaction, ensure any necessary frontend scripts are enqueued conditionally based on user roles.
  • Caching: Be mindful of WordPress caching mechanisms. If using page caching, the RBAC logic might not be applied correctly unless the cache is bypassed for logged-in users or specific roles. Consider using transient API or object cache for dynamic content fragments.
  • Security: Always sanitize and escape output. The restricted_content attribute, if it contains user-generated HTML, should be properly sanitized before rendering. For complex HTML, consider using wp_kses_post() or a more specifickses function.

By combining Gutenberg’s block API with robust PHP server-side rendering, you can create powerful, custom content management tools that adhere to strict access control policies.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Advanced Diagnostics: Identifying and fixing theme asset blocking in FSE Block Themes layouts
  • WordPress Development Recipe: High-efficiency server-side rendering for Gutenberg blocks using Strongly typed objects
  • Debugging Guide: Diagnosing caching race conditions in multi-site network environments with modern tools
  • Troubleshooting SQL query deadlocks in production when using modern Elementor custom widgets wrappers
  • How to analyze and reduce CPU consumption of custom Repository and Interface Structure event mediators

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (38)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (3)
  • WordPress Plugin Development (3)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Advanced Diagnostics: Identifying and fixing theme asset blocking in FSE Block Themes layouts
  • WordPress Development Recipe: High-efficiency server-side rendering for Gutenberg blocks using Strongly typed objects
  • Debugging Guide: Diagnosing caching race conditions in multi-site network environments with modern tools

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala