Hi,
I saw your question and it got me thinking. Ofcourse if we can create a group of users and we can restrict them with full builder access, but we cannot restrict users for specific pages or posts. I have developed a small plugin which can do this. It has got a small interface screen where we can select the users, pages, posts, etc. to restrict. Once installed, you can go to settings and can access the Bricks Access Manager screen. Unfortunately, I cannot attach the full plugin zip file here, hence adding the full code. Just create a folder named bricks-access-manager in the plugins folder and create a file bricks-access-manager.php and paste the entire code. Activate the plugin and your set.
A word of caution though: Please test the plugin thoroughly on a staging or local environment site and then migrate to a live production server.
<?php
/**
* Plugin Name: Bricks Access Manager
* Description: Per-user Bricks editing restrictions for posts, pages and custom post types.
* Version: 1.0.0
* Requires PHP: 7.4
* Author: Bricks Access Manager
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class Bricks_Access_Manager {
const OWNER_OPTION = 'bam_owner_id';
const RULES_OPTION = 'bam_restrictions';
public static function init() {
add_action( 'admin_menu', [ __CLASS__, 'menu' ] );
add_action( 'admin_init', [ __CLASS__, 'block_builder' ], 1 );
add_action( 'template_redirect', [ __CLASS__, 'block_builder' ], 1 );
add_action( 'admin_bar_menu', [ __CLASS__, 'admin_bar' ], 999 );
add_filter( 'post_row_actions', [ __CLASS__, 'row_actions' ], 999, 2 );
add_filter( 'page_row_actions', [ __CLASS__, 'row_actions' ], 999, 2 );
}
public static function activate() {
if ( ! get_option( self::OWNER_OPTION ) ) {
update_option(
self::OWNER_OPTION,
get_current_user_id(),
false
);
}
if ( false === get_option( self::RULES_OPTION ) ) {
add_option( self::RULES_OPTION, [], '', false );
}
}
private static function owner_id() {
return (int) get_option( self::OWNER_OPTION, 0 );
}
private static function is_owner() {
return get_current_user_id() === self::owner_id()
&& current_user_can( 'manage_options' );
}
private static function rules() {
$rules = get_option( self::RULES_OPTION, [] );
return is_array( $rules ) ? $rules : [];
}
private static function is_restricted( $post_id, $user_id = 0 ) {
$user_id = $user_id ?: get_current_user_id();
$post_id = absint( $post_id );
if ( ! $post_id || ! $user_id ) {
return false;
}
// The designated owner is never restricted.
if ( (int) $user_id === self::owner_id() ) {
return false;
}
$rules = self::rules();
return in_array(
$post_id,
array_map( 'absint', $rules[ $user_id ] ?? [] ),
true
);
}
public static function menu() {
if ( ! self::is_owner() ) {
return;
}
add_management_page(
'Bricks Access Manager',
'Bricks Access Manager',
'manage_options',
'bricks-access-manager',
[ __CLASS__, 'screen' ]
);
}
public static function screen() {
if ( ! self::is_owner() ) {
wp_die( 'Access denied.' );
}
$users = get_users( [
'orderby' => 'display_name',
'order' => 'ASC',
] );
$selected_user = isset( $_REQUEST['bam_user'] )
? absint( $_REQUEST['bam_user'] )
: 0;
if (
! $selected_user ||
$selected_user === self::owner_id() ||
! get_userdata( $selected_user )
) {
$selected_user = 0;
foreach ( $users as $user ) {
if ( (int) $user->ID !== self::owner_id() ) {
$selected_user = (int) $user->ID;
break;
}
}
}
$search = isset( $_GET['bam_search'] )
? sanitize_text_field( wp_unslash( $_GET['bam_search'] ) )
: '';
$post_type = isset( $_GET['bam_type'] )
? sanitize_key( $_GET['bam_type'] )
: 'all';
$page = max( 1, absint( $_GET['paged'] ?? 1 ) );
$types = get_post_types(
[ 'public' => true, 'show_ui' => true ],
'objects'
);
// Bricks templates are excluded from this version.
unset( $types['attachment'], $types['bricks_template'] );
if ( $post_type !== 'all' && ! isset( $types[ $post_type ] ) ) {
$post_type = 'all';
}
$query = new WP_Query( [
'post_type' => $post_type === 'all'
? array_keys( $types )
: $post_type,
'post_status' => [ 'publish', 'draft', 'private', 'pending', 'future' ],
'posts_per_page' => 25,
'paged' => $page,
's' => $search,
'orderby' => 'title',
'order' => 'ASC',
] );
$rules = self::rules();
if (
$_SERVER['REQUEST_METHOD'] === 'POST' &&
isset( $_POST['bam_save'] )
) {
check_admin_referer( 'bam_save_rules' );
$posted_user = absint( $_POST['bam_user'] ?? 0 );
if (
$posted_user &&
$posted_user === $selected_user &&
$posted_user !== self::owner_id() &&
get_userdata( $posted_user )
) {
$visible_ids = isset( $_POST['bam_visible'] )
? array_map( 'absint', (array) wp_unslash( $_POST['bam_visible'] ) )
: [];
$checked_ids = isset( $_POST['bam_locked'] )
? array_map( 'absint', (array) wp_unslash( $_POST['bam_locked'] ) )
: [];
// Only modify the items displayed on this page.
$existing = array_map(
'absint',
$rules[ $posted_user ] ?? []
);
$existing = array_diff( $existing, $visible_ids );
$checked_ids = array_intersect(
$checked_ids,
$visible_ids
);
$rules[ $posted_user ] = array_values(
array_unique(
array_merge( $existing, $checked_ids )
)
);
update_option( self::RULES_OPTION, $rules, false );
echo '<div class="notice notice-success is-dismissible"><p>Restrictions saved.</p></div>';
}
}
$locked = array_map(
'absint',
$rules[ $selected_user ] ?? []
);
$base_url = admin_url( 'tools.php?page=bricks-access-manager' );
?>
<div class="wrap">
<h1>Bricks Access Manager</h1>
<p>
Select a user and restrict Bricks editing
for individual pages and posts.
Normal WordPress editing remains available.
</p>
<form method="get" style="margin:20px 0;">
<input type="hidden" name="page"
value="bricks-access-manager">
<label for="bam_user"><strong>User:</strong></label>
<select name="bam_user" id="bam_user">
<?php foreach ( $users as $user ) :
if ( (int) $user->ID === self::owner_id() ) {
continue;
}
?>
<option value="<?php echo esc_attr( $user->ID ); ?>"
<?php selected( $selected_user, $user->ID ); ?>>
<?php echo esc_html( $user->display_name . ' (' . $user->user_login . ')' ); ?>
</option>
<?php endforeach; ?>
</select>
<label for="bam_type">Content type:</label>
<select name="bam_type" id="bam_type">
<option value="all">All types</option>
<?php foreach ( $types as $type ) : ?>
<option value="<?php echo esc_attr( $type->name ); ?>"
<?php selected( $post_type, $type->name ); ?>>
<?php echo esc_html( $type->labels->name ); ?>
</option>
<?php endforeach; ?>
</select>
<input type="search"
name="bam_search"
value="<?php echo esc_attr( $search ); ?>"
placeholder="Search content">
<?php submit_button( 'Filter', 'secondary', '', false ); ?>
</form>
<?php if ( $selected_user ) : ?>
<form method="post">
<?php wp_nonce_field( 'bam_save_rules' ); ?>
<input type="hidden" name="bam_user"
value="<?php echo esc_attr( $selected_user ); ?>">
<p>
<strong><?php echo count( $locked ); ?></strong>
restricted items for this user.
</p>
<p>
<button type="button" class="button"
id="bam-select-all">Restrict all visible</button>
<button type="button" class="button"
id="bam-clear-all">Allow all visible</button>
</p>
<table class="widefat striped">
<thead>
<tr>
<th>Content</th>
<th>Type</th>
<th>Status</th>
<th>Restrict Bricks</th>
</tr>
</thead>
<tbody>
<?php if ( $query->have_posts() ) : ?>
<?php foreach ( $query->posts as $post ) : ?>
<tr>
<td>
<strong>
<?php echo esc_html( get_the_title( $post ) ?: '(No title)' ); ?>
</strong>
<input type="hidden"
name="bam_visible[]"
value="<?php echo esc_attr( $post->ID ); ?>">
</td>
<td>
<?php echo esc_html( get_post_type_object( $post->post_type )->labels->singular_name ); ?>
</td>
<td>
<?php echo esc_html( $post->post_status ); ?>
</td>
<td>
<input type="checkbox"
class="bam-checkbox"
name="bam_locked[]"
value="<?php echo esc_attr( $post->ID ); ?>"
<?php checked( in_array( $post->ID, $locked, true ) ); ?>>
</td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr><td colspan="4">No content found.</td></tr>
<?php endif; ?>
</tbody>
</table>
<p>
<?php submit_button(
'Save Restrictions',
'primary',
'bam_save',
false
); ?>
</p>
</form>
<?php
$pagination = paginate_links( [
'base' => add_query_arg(
[
'bam_user' => $selected_user,
'bam_type' => $post_type,
'bam_search' => $search,
'paged' => '%#%',
],
$base_url
),
'format' => '',
'current' => $page,
'total' => $query->max_num_pages,
'type' => 'plain',
] );
if ( $pagination ) {
echo '<div class="tablenav"><div class="tablenav-pages">';
echo wp_kses_post( $pagination );
echo '</div></div>';
}
?>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const checkboxes = document.querySelectorAll('.bam-checkbox');
document.getElementById('bam-select-all')
?.addEventListener('click', function () {
checkboxes.forEach(el => el.checked = true);
});
document.getElementById('bam-clear-all')
?.addEventListener('click', function () {
checkboxes.forEach(el => el.checked = false);
});
});
</script>
<?php
wp_reset_postdata();
}
/**
* Block the standard Bricks builder URL.
*
* This does not intercept every internal Bricks save endpoint.
*/
public static function block_builder() {
if ( ! is_user_logged_in() ) {
return;
}
if ( self::is_owner() ) {
return;
}
$bricks = isset( $_GET['bricks'] )
? sanitize_text_field( wp_unslash( $_GET['bricks'] ) )
: '';
if ( $bricks !== 'run' ) {
return;
}
$post_id = absint( $_GET['post'] ?? 0 );
if ( ! $post_id && ! is_admin() ) {
$post_id = get_queried_object_id();
}
if ( self::is_restricted( $post_id ) ) {
wp_die(
'You do not have permission to edit this content with Bricks.',
'Bricks Access Restricted',
[ 'response' => 403 ]
);
}
}
/**
* Remove Bricks-related row actions.
*/
public static function row_actions( $actions, $post ) {
if ( ! self::is_restricted( $post->ID ) ) {
return $actions;
}
foreach ( $actions as $key => $html ) {
if (
stripos( $key, 'bricks' ) !== false ||
stripos( $html, 'bricks=run' ) !== false
) {
unset( $actions[ $key ] );
}
}
return $actions;
}
/**
* Remove Bricks builder links from the admin bar
* for restricted content.
*/
public static function admin_bar( $bar ) {
if ( self::is_owner() ) {
return;
}
$post_id = get_queried_object_id();
if ( ! $post_id && is_admin() ) {
$post_id = absint( $_GET['post'] ?? 0 );
}
if ( ! self::is_restricted( $post_id ) ) {
return;
}
foreach ( (array) $bar->get_nodes() as $node ) {
if (
stripos( $node->id, 'bricks' ) !== false ||
(
! empty( $node->href ) &&
stripos( $node->href, 'bricks=run' ) !== false
)
) {
$bar->remove_node( $node->id );
}
}
}
}
register_activation_hook(
__FILE__,
[ 'Bricks_Access_Manager', 'activate' ]
);
Bricks_Access_Manager::init();