How to customize get_the_post_navigation in theme - wordpress

get_the_post_navigation() function is defined inside wp-includes/link-template.php
How can I override it in my theme?

get_the_post_navigation does not utilize any filters so there is no easy way to modify or override all of its output.
While get_the_post_navigation does not apply any filter itself, it does call functions that do apply filter. Specifically get_adjacent_post_link:
return apply_filters( "{$adjacent}_post_link", $output, $format, $link, $post );
Hooking into that filter will allow you to override some, but not all, of the output. get_the_post_navigation also uses _navigation_markup which does not apply any filters. That portion of the output cannot be overridden.
You could request that a filter be added to that function. It would be a simple update and it would enable you to override all of the output.
function _navigation_markup( $links, $class = 'posts-navigation', $screen_reader_text = '' ) {
if ( empty( $screen_reader_text ) ) {
$screen_reader_text = __( 'Posts navigation' );
}
$template = '
<nav class="navigation %1$s" role="navigation">
<h2 class="screen-reader-text">%2$s</h2>
<div class="nav-links">%3$s</div>
</nav>';
//Add this line
$template = apply_filters('navigation_markup_template', $template);
return sprintf( $template, sanitize_html_class( $class ), esc_html( $screen_reader_text ), $links );
}
An easier solution might just be to create your own post navigation function, and then replace all references to get_the_post_navigation in your theme with your function.

Related

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();

Timber library doesn't support "nav_menu_link_attributes" filter?

I'm trying to use nav_menu_link_attributes to append something else to the "href" of a single menu item. Seems to me Timber library doesn't supports this filter?
My code works as expected with twentytwenty, however it does not takes effect to change the attribute when I switch the theme to Gesso-WP, that depends on Timber library. Also I cannot find any other filter than "nav_menu_css_class" inside the MenuItem class https://github.com/timber/timber/blob/master/lib/MenuItem.php
function filter_menu_item_href( $atts ) {
if ( strpos( $atts['href'], 'site/SPageNavigator/my_account.html' ) !== false ) {
$atts['href'] .= '?NEXTURL=' . home_url();
}
return $atts;
}
add_filter( 'nav_menu_link_attributes', 'filter_menu_item_href' );
What are the filtering options on Timber to change this single attribute value?
I finally handled this by using "wp_nav_menu_objects". This filter is present in the Menu class from the Timber library plugin: https://github.com/timber/timber/blob/master/lib/Menu.php
function filter_menu_item_href( $menu ) {
foreach ( $menu as $item ) {
if ( strpos( $item->url, 'site/SPageNavigator/my_account.html' ) !== false ) {
$item->url .= '?NEXTURL=' . home_url();
}
}
return $menu;
}
add_filter( 'wp_nav_menu_objects', 'filter_menu_item_href' );

Woocommerce - Display single product attribute(s) with shortcodes in Frontend

i've read many Q/A's here during the last few days, but unfortunately none of them solved my issue.
I'm trying to fetch product attributes and display them on the frontend with a shortcode. I have managed to display ALL available attributes and display them in a list, but i need to select only one or two of them in different locations (thats why using shortcodes). For example like [shortcode_attribute name="brand"].
Any help is highly appreciated!
my code so far:
function tutsplus_list_attributes( $product ) {
global $product;
global $post;
$attributes = $product->get_attributes();
if ( ! $attributes ) {
return;
}
foreach ( $attributes as $attribute ) {
// Get the taxonomy.
$terms = wp_get_post_terms( $product->id, $attribute[ 'name' ], 'all' );
$taxonomy = $terms[ 0 ]->taxonomy;
// Get the taxonomy object.
$taxonomy_object = get_taxonomy( $taxonomy );
// Get the attribute label.
$attribute_label = $taxonomy_object->labels->name;
// Display the label followed by a clickable list of terms.
echo get_the_term_list( $post->ID, $attribute[ 'name' ] , '<div><li class="bullet-arrow">' . $attribute_label . ': ' , ', ', '</li></div>' );
}
}
add_action( 'woocommerce_product_meta_end', 'tutsplus_list_attributes' );
add_shortcode('display_attributes', 'tutsplus_list_attributes');
While I am still not 100% clear what you're after, what I'm going to create is a shortcode that creates a list of all terms in all attributes. To make it more flexible I'll add support for a shortcode parameter so you can create a list of specific attribute terms.
One major thing you need to alter from your code, is that shortcodes need to RETURN a string and not ECHO out html. Echoing out shortcodes can result in unexpected weirdness.
/**
* Attributes shortcode callback.
*/
function so_39394127_attributes_shortcode( $atts ) {
global $product;
if( ! is_object( $product ) || ! $product->has_attributes() ){
return;
}
// parse the shortcode attributes
$args = shortcode_atts( array(
'attributes' => array_keys( $product->get_attributes() ), // by default show all attributes
), $atts );
// is pass an attributes param, turn into array
if( is_string( $args['attributes'] ) ){
$args['attributes'] = array_map( 'trim', explode( '|' , $args['attributes'] ) );
}
// start with a null string because shortcodes need to return not echo a value
$html = '';
if( ! empty( $args['attributes'] ) ){
foreach ( $args['attributes'] as $attribute ) {
// get the WC-standard attribute taxonomy name
$taxonomy = strpos( $attribute, 'pa_' ) === false ? wc_attribute_taxonomy_name( $attribute ) : $attribute;
if( taxonomy_is_product_attribute( $taxonomy ) ){
// Get the attribute label.
$attribute_label = wc_attribute_label( $taxonomy );
// Build the html string with the label followed by a clickable list of terms.
// Updated for WC3.0 to use getters instead of directly accessing property.
$html .= get_the_term_list( $product->get_id(), $taxonomy, '<li class="bullet-arrow">' . $attribute_label . ': ' , ', ', '</li>' );
}
}
// if we have anything to display, wrap it in a <ul> for proper markup
// OR: delete these lines if you only wish to return the <li> elements
if( $html ){
$html = '<ul class="product-attributes">' . $html . '</ul>';
}
}
return $html;
}
add_shortcode( 'display_attributes', 'so_39394127_attributes_shortcode' );
Usage: This would be used in 1 of 2 ways.
First, to display specific attributes use:
[display_attributes attributes="color|material"]
Second, to display all attributes use:
[display_attributes]
Edit
Here's an always single display edition:
/**
* Attribute shortcode callback.
*/
function so_39394127_singular_attribute_shortcode( $atts ) {
global $product;
if( ! is_object( $product ) || ! $product->has_attributes() ){
return;
}
// parse the shortcode attributes
$args = shortcode_atts( array(
'attribute' => ''
), $atts );
// start with a null string because shortcodes need to return not echo a value
$html = '';
if( $args['attribute'] ){
// get the WC-standard attribute taxonomy name
$taxonomy = strpos( $args['attribute'], 'pa_' ) === false ? wc_attribute_taxonomy_name( $args['attribute'] ) : $args['attribute'];
if( taxonomy_is_product_attribute( $taxonomy ) ){
// Get the attribute label.
$attribute_label = wc_attribute_label( $taxonomy );
// Build the html string with the label followed by a clickable list of terms.
// Updated for WC3.0 to use getters instead of directly accessing property.
$html .= get_the_term_list( $product->get_id(), $taxonomy, $attribute_label . ': ' , ', ', '' );
}
}
return $html;
}
add_shortcode( 'display_attribute', 'so_39394127_singular_attribute_shortcode' );
This removes all HTML markup, and so you'd need to provide your own... which would happen if you are making a custom list.
<ul>
<li class="arrow">Static List Item</li>
<li class="arrow">[display_attribute attribute="color"]</li>
<ul>
EDIT: This code can be added to your theme's functions.php, but preferably either in a site-specific plugin or via the Code Snippets plugin.
If anyone else just wants to simply output the attribute value as text, replace the $html .= line with this:
$html .= strip_tags(get_the_term_list($product->get_id(), $taxonomy));
#helgatheviking There must be a more elegant way than this! :)
Needed this recently - found https://wordpress.org/plugins/wcpas-product-attributes-shortcode/ does the trick.
With this plugin you can set an "attribute" parameter to match one of the product attributes you have setup.
For example if you have a brand attribute you can use:
[wcpas_product_attributes attribute="brand"]
This will output a <ul> list containing all brands.
There are also a number of parameters you can use alongside the attribute including:
orderby (order the list by an orderby value)
order (order asc, desc, rand, etc)
hide_empty (hide empty terms)
show_counts (show the total number of products in the brand)
archive_links (use an archive based URL instead of a filter based URL)
min_price (When using archive_links = 0 you can have a price filter based URL off a minimum price)
max_price (When using archive_links = 0 you can have a price filter based URL off a maximum price)

WooCommerce override function

I'd like to override a function in woocommerce, specifically -
woocommerce/includes/wc-cart-functions.php (the wc_cart_totals_order_total_html function).
I could edit the function directly (it outputs html that needs to be changed), but I'd prefer not to as I'll lose changes on updates.
I'm sure it's a common thing, I'm just not quite sure how to go about doing that. If I copy the function to functions.php in my theme, I get an error about re-declaring the function.
It's an old topic, but maybe I could help a bit. I had similar problem. I wanted to override currencies, and add a custom currency. The functions are in woocommerce/includes/wc-core-functions.php
function get_woocommerce_currencies() {
return array_unique(
apply_filters( 'woocommerce_currencies',
array(
The other function is:
function get_woocommerce_currency_symbol( $currency = '' ) {
if ( ! $currency ) {
$currency = get_woocommerce_currency();
}
switch ( $currency ) {
...
return apply_filters( 'woocommerce_currency_symbol', $currency_symbol, $currency );
This is the code I've put in functions.php of my child theme:
add_filter( 'woocommerce_currencies', 'add_my_currency' );
function add_my_currency( $currencies ) {
$currencies['RSD'] = __( 'Serbian dinar', 'woocommerce' );
return $currencies;
}

Woocommerce add_action not working

I want to replace the product thumbnail based on stock. This works if I plop it directly into the wc-template-functions woocommerce_get_product_thumbnail function, but not when I try to hook into it from functions.php. This is my code:
function sold_out_thumb ($size = 'shop_catalog', $placeholder_width = 0, $placeholder_height = 0 ) {
global $product;
global $post;
if ( has_post_thumbnail() )
if ($product->price > 0 && !$product->is_in_stock() )
return get_the_post_thumbnail( $post->ID, $size,array( 'class' => 'lighter' ) );
else
return get_the_post_thumbnail( $post->ID, $size );
elseif ( wc_placeholder_img_src() )
return wc_placeholder_img( $size );
}
add_action ('woocommerce_get_product_thumbnail','sold_out_thumb',90);
Any ideas?
As you can see in wc-template-functions.php, almost all template functions are inside if ( !function_exists() ) condition. That way the creators of the plugin enabled us to override template functions, so just create woocommerce_get_product_thumbnail() function in functions.php, and the original will not be loaded.
There is no action called woocommerce_get_product_thumbnail which is why this isn't working for you. This is actually a function called woocommerce_get_product_thumbnail(). If you look at the code there is no action hook.

Resources