get_all_registered();
foreach ( $registered_blocks as $name => $block ) {
$block->attributes['contentControls'] = [
'type' => 'object',
'default' => [
'enabled' => false,
'rules' => [],
],
];
}
}
/**
* Check if block has controls enabled.
*
* @param array $block Block to be checked.
* @return boolean Whether the block has Controls enabled.
*/
public function has_block_controls( $block ) {
if ( ! isset( $block['attrs']['contentControls'] ) ) {
return false;
}
$controls = wp_parse_args( $block['attrs']['contentControls'], [
'enabled' => false,
] );
return ! ! $controls['enabled'];
}
/**
* Get blocks controls if enabled.
*
* @param array $block Block to get controls from.
* @return array|null Controls if enabled.
*/
public function get_block_controls( $block ) {
if ( ! $this->has_block_controls( $block ) ) {
return null;
}
return wp_parse_args( $block['attrs']['contentControls'], [
'enabled' => false,
'rules' => [],
] );
}
/**
* Short curcuit block rendering for hidden blocks.
*
* @param string|null $pre_render The pre-rendered content. Default null.
* @param array $parsed_block The block being rendered.
* @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block.
*
* @return string|null
*/
public function pre_render_block( $pre_render, $parsed_block, $parent_block ) {
if ( 'core/navigation' === $parsed_block['blockName'] ) {
$nav_menu_ref = ! empty( $parsed_block['attrs']['ref'] ) ? $parsed_block['attrs']['ref'] : 0;
if ( $nav_menu_ref ) {
$navigation_post = get_post( $nav_menu_ref );
$parsed_blocks = parse_blocks( $navigation_post->post_content );
}
// TODO for some reason, controls applied to core/navigation-link are not being saved, or not appearing in attrs.
}
if ( ! isset( $parsed_block['attrs']['contentControls'] ) ) {
return $pre_render;
}
if ( protection_is_disabled() ) {
return $pre_render;
}
$controls = wp_parse_args( $parsed_block['attrs']['contentControls'], [
'enabled' => false,
'rules' => [],
] );
if ( ! $controls['enabled'] ) {
return $pre_render;
}
/**
* Filter whether to hide the block.
*
* @param bool $should_hide Whether the block should be hidden.
* @param array $rules Rules to check.
* @param array $parsed_block The block being rendered.
* @return bool
*/
$should_hide = apply_filters(
'content_control/should_hide_block',
false,
$controls['rules'],
$parsed_block
);
return $should_hide ? '' : $pre_render;
}
/**
* Check block rules to see if it should be hidden from user.
*
* @param bool $should_hide Whether the block should be hidden.
* @param array $rules Rules to check.
* @return bool
*/
public function block_user_rules( $should_hide, $rules ) {
$rules = wp_parse_args( $rules, [
'user' => null,
] );
if ( null === $rules['user'] || true === $should_hide ) {
return $should_hide;
}
$user_status = ! empty( $rules['user']['userStatus'] ) ? $rules['user']['userStatus'] : false;
$role_match = ! empty( $rules['user']['roleMatch'] ) ? $rules['user']['roleMatch'] : 'any';
$user_roles = ! empty( $rules['user']['userRoles'] ) ? $rules['user']['userRoles'] : [];
if ( ! \ContentControl\user_meets_requirements( $user_status, $user_roles, $role_match ) ) {
return true;
}
return $should_hide;
}
/**
* Get any classes to be added to the outer block element.
*
* @param array $block Block to get classes for.
* @return null|array
*/
public function get_block_control_classes( $block ) {
if ( ! $this->has_block_controls( $block ) ) {
return null;
}
$classes = [];
$controls = $this->get_block_controls( $block );
if ( isset( $controls['rules']['device'] ) ) {
$device_rules = wp_parse_args( $controls['rules']['device'], [
'hideOn' => [],
] );
$hide_on = wp_parse_args( $device_rules['hideOn'], [
'mobile' => null,
'tablet' => null,
'desktop' => null,
] );
foreach ( $hide_on as $device => $hidden ) {
if ( $hidden ) {
$classes[] = 'cc-hide-on-' . esc_attr( $device );
}
}
}
/**
* Filter the classes to be added to the block.
*
* @param array $classes Classes to be added.
* @param array $block Block to get classes for.
*
* @return string[]
*/
$classes = apply_filters( 'content_control/get_block_control_classes', $classes, $block );
return array_unique( $classes );
}
/**
* Filter the block attributes, primarily to add classes and control visibility.
*
* References: https://github.com/WordPress/gutenberg/search?l=PHP&q=%27render_block%27
* - https://github.com/WordPress/gutenberg/blob/9aab0c4f60c78d19aae0af3351a2b66f8fa4c162/lib/block-supports/layout.php#L317
* - https://github.com/WordPress/gutenberg/blob/e776b4f00f690ce9cf21c027ebf5e7442420d716/lib/block-supports/duotone.php#L503
* - https://github.com/WordPress/gutenberg/blob/9aab0c4f60c78d19aae0af3351a2b66f8fa4c162/lib/block-supports/elements.php#L54-L72
*
* @param string $block_content Blocks rendered html.
* @param array $block Array of block properties.
*
* @return string
*/
public function render_block( $block_content, $block ) {
if ( ! $this->has_block_controls( $block ) ) {
return $block_content;
}
$classes = $this->get_block_control_classes( $block );
if ( empty( $classes ) ) {
return $block_content;
}
// Enqueue the styles.
// wp_enqueue_style( 'content-control-block-styles' );.
$class_name = implode( ' ', $classes );
/** Mimicing WP Cores usage in https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/block-supports/elements.php#L32 */
$html_element_matches = [];
preg_match( '/<[^>]+>/', $block_content, $html_element_matches, PREG_OFFSET_CAPTURE );
$first_element = $html_element_matches[0][0];
// If the first HTML element has a class attribute just add the new class
// as we do on layout and duotone.
if ( strpos( $first_element, 'class="' ) !== false || strpos( $first_element, "class='" ) !== false ) {
$content = preg_replace(
// Matches $1 & $3 is ' or ", $2 are the existing classes.
'/class=([\'"])([a-z0-9-_ ]*)([\'"])/',
'class=$1$2 ' . $class_name . '$3',
$block_content,
1
);
} else {
// If the first HTML element has no class attribute we should inject the attribute before the attribute at the end.
$first_element_offset = $html_element_matches[0][1];
$content = substr_replace( $block_content, ' class="' . $class_name . '"', $first_element_offset + strlen( $first_element ) - 1, 0 );
}
return $content;
}
/**
* Print the styles for the block controls.
*
* @return void
*/
public function print_block_styles() {
$media_queries = $this->container->get_option( 'mediaQueries' );
if ( ! $media_queries ) {
?>
$media_query_settings ) {
$breakpoint = $media_query_settings['breakpoint'];
$style = <<