Using render_block only on Gutenberg blocks which aren't nested - wordpress

I want to add a wrapper div to Gutenberg blocks so I can do things like full width images and other custom ACF blocks. I found this on the WP documentation:
function block_wrap_filter( $block_content, $block ) {
if ( empty( trim( $block_content ) ) ) {
return $block_content;
}
$blockid = $block['id'];
echo $blockid;
return sprintf(
'<section class="outer-%1$s"><div class="inner">%2$s</div></section>',
sanitize_title( $block['blockName'] ),
$block_content
);
}
add_filter( 'render_block', 'block_wrap_filter', 10, 2 );
This works great except for the fact that it will apply the wrappers to nested blocks. Is there anyway to filter it so it doesn't apply to blocks that are within other blocks?
I've looked to see if there's any hooks or if the $block var lists itself as a child or anything but can't see anything I can make use of there.
I thought maybe if I could get the ID of the block and then see if the child has a related ID, then maybe I could use some sort of php if statement to wrap the filter. But I haven't been able to get $block to give an ID.

Related

Separate attribute term from variable dropdown on single product page

I'm working on a child theme where I want to split variable product into 2 elements:
Regular variation form with a dropdown where customer gets to pick from a single attribute with multiple terms except for one.
A single form with just one specific term and its own add to cart button.
For example:
When selling clothes we create "tshirts" attribute and use terms to describe both the ones that have prints on them (print 1, print 2, print3 etc.) as well as a regular, plain tshirt (which has a special "plain-shirt" term) and unlike others - doesn't appear in the dropdown but is a standalone element.
Attached image
I've tried looking into different solutions and eventually settled down on editing variable.php themplate, duplicating the variations_form and using code from this thread: Hide specific product attribute terms on WooCommerce variable product dropdown to filter out necessary terms. However, since 'wc_dropdown_variation_attribute_options' fucntion and hooks are already inside the form I'm struggling with how should I go about this. It is probably an extremely wonky approach in my case so any help, ideas and suggestions will be greatly appreciated.
Edit: Found a working soultion by further editing variable.php template:
For the first form I added a filter before "woocommerce_before_variations_form" hook:
add_filter( 'woocommerce_dropdown_variation_attribute_options_args', 'sample_dropdown', 10, 1 );
function sample_dropdown( $args ) {
$taxonomy = 'pa_capacity';
$targeted_terms_names = array( "plain-shirt", "plain-tshirt" );
$terms_slugs = array_filter( array_map( 'sanitize_title', $targeted_terms_names ) );
if( $args['attribute'] === $taxonomy ) {
foreach( $args['options'] as $key => $option ){
if( ! in_array( $option , $terms_slugs ) ) {
unset($args['options'][$key]);
}
}
}
return $args;
}
and removed filer on "woocommerce_after_variations_form" hook.
Then for the second form I applied another filter with reversed array
if( in_array( $option , $terms_slugs )
Instead of
if( ! in_array( $option , $terms_slugs ) )
Its' still very untidy but at least it works.

Inline styles for Gutenberg blocks not rendering when content fetched with AJAX request

I need to grab the_content() via an AJAX request and render all Gutenberg blocks with their inline styling in the page.
Problem is, unique block classes are added to the footer in theme templates.
.wp-container-5 {
display: flex;
gap: 2em;
flex-wrap: nowrap;
align-items: center;
}
When get_the_content() is used via an AJAX request, that unique block styling isn't rendered. I would guess this is because the inline block styling relies on a hook of some sort that doesn't get fired with an AJAX request. do_blocks() does not render the inline styling.
I've searched the database and scoured the WordPress source files and cannot find where classes like .wp-container-5 are coming from. I thought if I could find the location of the inline styling, I could simply query it and render it in the page.
Does anyone know where the unique block styles are stored and/or how to query them and include them via an AJAX request?
I managed to solve this after many hours of frustration.
In wp-includes/block-supports/layout.php there is a function called wp_render_layout_support_flag(). This function takes a block's content and a block object and assigns the unique class wp-container- with a unique ID at the end. It then renders the inline styling with the function wp_get_layout_style() and enqueues that styling with wp_enqueue_block_support_styles().
Problem is, wp_render_layout_support_flag() doesn't return the styling. It returns the block content with CSS classes and inserts the styling into the footer with CSS classes that match. So, it's not as simple as just calling wp_get_layout_style() because a unique ID is assigned in wp_render_layout_support_flag() that is only matched when wp_get_layout_style() is called inside the wp_render_layout_support_flag() function.
The solution was to copy and paste (not ideal but it works) the wp_render_layout_support_flag() function and slightly alter it.
function my_render_layout_support_flag( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$support_layout = block_has_support( $block_type, array( '__experimentalLayout' ), false );
if ( ! $support_layout ) {
return $block_content;
}
$block_gap = wp_get_global_settings( array( 'spacing', 'blockGap' ) );
$default_layout = wp_get_global_settings( array( 'layout' ) );
$has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false;
$default_block_layout = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() );
$used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout;
if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] ) {
if ( ! $default_layout ) {
return $block_content;
}
$used_layout = $default_layout;
}
$class_name = wp_unique_id( 'wp-container-' );
$gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) );
// Skip if gap value contains unsupported characters.
// Regex for CSS value borrowed from `safecss_filter_attr`, and used here
// because we only want to match against the value, not the CSS attribute.
if ( is_array( $gap_value ) ) {
foreach ( $gap_value as $key => $value ) {
$gap_value[ $key ] = $value && preg_match( '%[\\\(&=}]|/\*%', $value ) ? null : $value;
}
} else {
$gap_value = $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value;
}
$fallback_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), '0.5em' );
// If a block's block.json skips serialization for spacing or spacing.blockGap,
// don't apply the user-defined value to the styles.
$should_skip_gap_serialization = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' );
$style = wp_get_layout_style( ".$class_name", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value );
// This assumes the hook only applies to blocks with a single wrapper.
// I think this is a reasonable limitation for that particular hook.
$content = preg_replace(
'/' . preg_quote( 'class="', '/' ) . '/',
'class="' . esc_attr( $class_name ) . ' ',
$block_content,
1
);
// This is where the changes happen
return '<style>' . $style . '</style>' . $content;
}
The only change is near the end where wp_enqueue_block_support_styles() was removed and now styling and content are returned.
Now, Gutenberg blocks can be rendered and have the proper styling when using an AJAX call!
$content = get_the_content(null, true, $post_id);
$blocks = parse_blocks( $content );
foreach ($blocks as $block) {
echo my_render_layout_support_flag( render_block($block), $block );
}
This solution feels kind of ridiculous but it works... WordPress really should support the rendering of blocks asynchronously.
I have not tested this within the response from an AJAX request using the old admin-ajax.php method, however, I had been trying to accomplish this same thing within a response from the REST API.
Having found this thread and reading over the solution provided by Myles -- thanks for your details, btw -- and looking into the wp_render_layout_support_flag function a bit more, I came up with a potential alternative.
Generically, with the REST API, you could add something like this:
register_rest_field('post', 'styles', [
'get_callback' => function ($post) {
ob_start();
do_action('wp_footer');
$content = ob_get_clean();
// perform any needed manipulation
return $content;
},
'schema' => [
'description' => __('Styles'),
'type' => 'string',
],
]);
You'd probably want to strip out non-style content in the result, in the event you have other functions pushing content that would appear within wp_footer.
Perhaps something like this would get you what you are looking for:
ob_start();
the_content();
do_action('wp_footer');
$content = ob_get_clean();

Skip Certain Gutenberg Block in Loop

I have some WordPress Gutenberg blocks that I want to display in a different part of my theme. I know how to find the block and display it in my theme's template, but I don't know how to stop the block from showing with the rest of the blocks in the loop.
To clarify as an example: I have one block that I want to have in the admin area with the other blocks but I don't want to display it in the loop content. I need a way to skip it or filter it out in the normal loop. Then I use this code to output in another area of my theme:
function be_display_post_blockquote() {
global $post;
$blocks = parse_blocks( $post->post_content );
foreach( $blocks as $block ) {
if( 'lazyblock/area-2' === $block['blockName'] ) {
echo render_block( $block );
break;
}
}
}
The problem with the above code is it duplicates the block to show with the other "normal" loop content and also in my new location. Can you help me write a function/filter to stop specific blocks from showing up in the loop?
I figured this out...
Here is the function to filter the content of the loop and remove a specific block from the output because you already outputted the content of that specific block in another location in your theme or theme template file.
//If single block exists on page or post don't show it with the other blocks
function remove_blocks() {
// Check if we're inside the main loop in a post or page
if ( ( is_single() || is_page() ) && in_the_loop() && is_main_query() ) {
//parse the blocks so they can be run through the foreach loop
$blocks = parse_blocks( get_the_content() );
foreach ( $blocks as $block ) {
//look to see if your block is in the post content -> if yes continue past it if no then render block as normal
if ( 'lazyblock/top-of-page' === $block['blockName'] ) {
continue;
} else {
echo render_block( $block );
}
}
}
}
add_filter( 'the_content', 'remove_blocks');

How to use "has_term" correctly

Im working with the single product page and I need to have a different image (depending on the category) after the add to cart button.
I tried this code but it doesn't show me the image that I need
add_action ( 'woocommerce_after_add_to_cart_button', 'content_after_button' );
function content_after_button() {
if (has_term( 'Categoria', 'Accesorios' ) ) {
echo 'https://prueba.soygorrion.com.ar/wp-content/uploads/2019/08/iconos2.jpg';
}
I think im using has_term in the wrong way.
What im trying to accomplish is:
I have a parent category that is "Accesorios" and inside that I have other child categories like "Billeteras". For each one of this child categories it has to show a diferent image.
thank you
First of all there is issue with your has_term checking. If you check has_term docs, you can find it takes first parameter as category term and second parameter as taxonomy.
Add secondly you are just echo image url that is not going to display image.
So, do as follows -
add_action ( 'woocommerce_after_add_to_cart_button', 'content_after_button' );
function content_after_button() {
// do check with has_term( $term = '', $taxonomy = '', $post = null )
if( has_term( 'Accesorios', 'product_cat' ) ) {
echo '<img src="https://prueba.soygorrion.com.ar/wp-content/uploads/2019/08/iconos2.jpg" />';
}
}
Since you mentioned that the problem is with has_term function. If you can get the product ID inside add to cart, then you can use this code to get categories and check them:
$categories = wp_get_post_terms($product_id, 'product_cat');
if(in_array("Accesorios", $categories))
{
echo "bla bla";
}
I have tested this code for another purpose, it works fine. I hope this will help. Please inform me when you test it and if you face any errors update your question with your new code.

can't get apply_filter to work in wordpress thematic

I am learninig to create templates in wordpress using the thematic framework
In the header file I find:
// Filter provided for altering output of the header opening element
echo ( apply_filters( 'thematic_open_header', '<div id="header">' ) );
// Action hook creating the theme header
thematic_header();
echo ( apply_filters( 'thematic_close_header', '</div><!-- #header-->' ) );
I want to add a wrapper div element around the div id="header".../div created above
I have tried to edit the apply_filter lines like this:
echo ( apply_filters( 'thematic_open_header', '<div id="headerWrapper"><div id="header">' ) );
but it doesn't work, just printout out div id="header"
How can I get it to create the html that I want it to?
Thanks
Filters are used in functions.php or in a plugin like this:
add_filter( 'thematic_open_header', function(){
return '<div id="headerWrapper"><div id="header">';
});
The basic difference between apply_filters and do_action is that the first filters some value and returns something, while add_action will be used to do anything you want with the parameters sent or output something to the browser.
Some articles of interest: [1], [2], [3] and [4].

Resources