Hook in to post_gallery function to change the styles of gallery - wordpress

I have a problem that drives me mad, and can't find a solution to it in the documentation...
I want to hook in to Wordpress' post_gallery function to add a Class to the dl element that wraps the WP generated gallery individual images. By this I want to have the option to have full width images that can be controlled in the Attachments window of WP Media via ACF switch
if ( get_field('fullwidth') == 1 ){
$classname = 'fullwidth';
} else {
$classname = "";
}
Also setting the img "src" to the Large for these fullwidth images would come handy, so the page-load is not too slow.
Do I need to rewrite the whole gallery generating code from here?
https://core.trac.wordpress.org/browser/trunk/src/wp-includes/media.php
Can it be done via this method at all, or should I somehow try to hook in to the_content? But also for that method, I didn't find any info in regard how galleries are generated...

to add a Class to the dl element that wraps the WP generated gallery individual images
Instead of that, why don't you — using the Text/HTML editor — add the gallery like this:
<div class="fullwidth">
[gallery ... /]
</div>
(the three dots indicates the Shortcode parameters such as ids, size, and link)
And write some CSS code similar to:
.fullwidth .gallery-item {
/* Add styles for the DL (or FIGURE for HTML5-supported themes) element that wraps the gallery image and caption. */
}
.fullwidth .gallery-icon {
/* Add styles for the DT (or DIV for HTML5-supported themes) element that wraps the gallery image. */
}
.fullwidth .gallery-caption {
/* Add styles for the DD (or FIGCAPTION for HTML5-supported themes) element that wraps the gallery caption. */
}
[EDIT] Below is a custom Shortcode; used the exact same way you'd use the default [gallery] Shortcode.
Add these to the theme's functions.php file:
/**
* #param array $attr
* #param WP_Post $attachment
*/
function gallery_shortcode2_image_attributes( $attr, $attachment ) {
// Name of the custom field. (ACF)
$field_name = 'fullwidth';
if ( get_field( $field_name, $attachment->ID ) ) {
$attr['data-gallery-layout'] = 'full-width';
}
return $attr;
}
add_shortcode( 'gallery2', 'gallery_shortcode2' );
function gallery_shortcode2( $attr ) {
$atts = shortcode_atts( array(
'itemtag' => 'figure', // I'm assuming we'll always be using HTML5-supported themes..
'fwclass' => 'fullwidth', // The CSS class applied to items with, well, full-width layout.
'size' => 'medium_large', // You may change the default value; to "large", "custom", etc.
), $attr );
// Don't modify anything below unless you're 100% sure of what you're doing. =)
$itemtag = tag_escape( $atts['itemtag'] );
$valid_tags = wp_kses_allowed_html( 'post' );
if ( ! isset( $valid_tags[ $itemtag ] ) ) {
$itemtag = 'dl';
}
add_filter( 'wp_get_attachment_image_attributes', 'gallery_shortcode2_image_attributes', 10, 2 );
$output = gallery_shortcode( array_merge( $attr, $atts ) );
$output2 = '';
remove_filter( 'wp_get_attachment_image_attributes', 'gallery_shortcode2_image_attributes', 10, 2 );
$_tag = '<' . preg_quote( $itemtag ) . ' class=\'gallery-item\'>';
$arr = preg_split( "/($_tag)/", $output, -1, PREG_SPLIT_DELIM_CAPTURE );
for ( $i = 0, $j = 1; $i < count( $arr ); $i++, $j++ ) {
if ( $_tag === $arr[ $i ] && isset( $arr[ $j ] ) ) {
$class = 'gallery-item';
if ( preg_match( '/<img (.*?) data-gallery-layout="full-width"/', $arr[ $j ] ) ) {
$class .= ' ' . $atts['fwclass'];
}
$output2 .= '<' . $itemtag . ' class=\'' . esc_attr( $class ) . '\'>';
} else {
$output2 .= $arr[ $i ];
}
}
unset( $output, $arr );
return $output2;
}
And use [gallery2] instead of [gallery]; same arguments (e.g. ids and size), but with one extra argument for [gallery2], which is fwclass. (Refer to the gallery_shortcode() function for details on that argument.)
Hopefully [gallery2] works for you, like it did for me.
NOTE: Use [gallery2] only if you want to add a custom CSS class to the individual gallery item element (i.e. .gallery-item). Otherwise, use the default Shortcode — [gallery].

Related

Remove srcset attribute for cart-item-thumbnail-image on woocommerce cart page

Testing the cart page cross-browser I noticed that the cart-item-thumbnail image disappears on some instances, when adding, removing items or altering addresses on the cart page.
I found out, that the presence of the srcset attribute is causing this.
I tried to disable the srcset attribute for the cart page like so:
function cart_disable_srcset() {
if ( is_cart()) {
return false;
}
}
add_filter( 'wp_calculate_image_srcset', 'cart_disable_srcset' );
The codition if ( is_cart()) is ignored, and the srcset is removed site-wide.
Then I found a function, that removes the srcset for the_post_thumbnail( 'full' ) and
the_post_thumbnail():
/**
* Remove the srcset attribute from post thumbnails
* that are called with the 'full' size string: the_post_thumbnail( 'full' )
*
* #link http://wordpress.stackexchange.com/a/214071/26350
*/
add_filter( 'post_thumbnail_size', function( $size )
{
if( is_string( $size ) && 'full' === $size )
add_filter(
'wp_calculate_image_srcset_meta',
'__return_null_and_remove_current_filter'
);
return $size;
} );
// Would be handy, in this example, to have this as a core function ;-)
function __return_null_and_remove_current_filter ( $var )
{
remove_filter( current_filter(), __FUNCTION__ );
return null;
}
In my cart.php template if have the following line:
$thumbnail = apply_filters( 'woocommerce_cart_item_thumbnail', $_product->get_image('my_small_size'), $cart_item, $cart_item_key );
I tried to use this with the function mentioned above:
add_filter( 'woocommerce_cart_item_thumbnail', function( $size )
{
if( is_string( $size ) && 'my_small_size' === $size )
add_filter(
'wp_calculate_image_srcset_meta',
'__return_null_and_remove_current_filter'
);
return $size;
} );
But that doesn’t bear the desired result.
How can I configure the above mentioned function to remove the srcset for the cart-item-thumbnail and all other images that are referenced with my_small_size? Can this be achieved at all with this function, or is there another way?
Hook into wp_calculate_image_srcset read: detailed explanation.
$source is an array that holds all images for the srcset attribute.
The custom image size 65*65, which is displayed on my cart-page, can be found by conditionally checking the $size_array. If the $size_array has 65, then unset all unwanted sizes.
The srcset is gone. No more vanishing cart-item-thumbnails when altering the cart page items on certain browsers. The rest is configuring the srcset according to my needs. It may be helpful as an example.
add_filter( 'wp_calculate_image_srcset', 'my_custom_image_srcset', 10, 5);
function my_custom_image_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id) {
//remove unwanted sizes from $source array
$remove = ['150', '300'];
$sources = array_diff_key($sources, array_flip($remove));
//remove unwanted sizes from $source array if thumbnail width is 65
if ($size_array[0] === 65){
$remove = ['160', '490','768','800'];
$sources = array_diff_key($sources, array_flip($remove));
}
//remove unwanted sizes for archive pages
else if (is_archive()){
$remove = ['65', '768','800'];
$sources = array_diff_key($sources, array_flip($remove));
}
//remove unwanted sizes for product pages
else if (is_product()){
$remove = ['65', '160'];
$sources = array_diff_key($sources, array_flip($remove));
}
//check results
//print_r($size_array);
//print_r($sources);
return $sources;
}
Unfortunately nobody commented the function mentioned in the question.
But this hook works for me.

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

How do I access vc_map() internal values to create custom markup for WPBakery interface?

A similar question has been asked here, without any viable responses: Visual Composer custom markup for custom shortcode (vc_map)
and here: Visual Composer custom shortcode template - custom_markup display user input
I am trying to create custom markup within the WPBakery interface.
I can add custom markup no problem by doing:
$markup = 'test';
vc_map( array(
"name" => __("MyShortcode"),
"base" => "myshortcode",
"category" => __('Content'),
"custom_markup" => $markup, // #TODO how do we access shortcode's attributes here to display in bakery grid
"params" => $params
) );
This will output "test" in the WPBakery grid which is good, but how do I access vc_map() internal values to display?
For instance I have "post types" as a field for this shortcode. If someone selects "page" post type for example, I would like to display those posts within the WPBakery grid. What I cannot figure out is how to get the values that the user selected to display.
Any help would be greatly appreciated. I've searched endlessly on this one.
I found a solution. By extending the class WPBakeryShortCode, you could change the contentAdmin output :
// Extend Class WPBakeryShortCodesContainer
if (class_exists('WPBakeryShortCode')) {
class WPBakeryShortCode_Custom_Element extends WPBakeryShortCode {
/**
* #param $atts
* #param $content
*
* #return string
* #throws \Exception
*/
public function contentAdmin( $atts, $content = null ) {
$output = $custom_markup = $width = $el_position = '';
if ( null !== $content ) {
$content = wpautop( stripslashes( $content ) );
}
$shortcode_attributes = array( 'width' => '1/1' );
$atts = vc_map_get_attributes( $this->getShortcode(), $atts ) + $shortcode_attributes;
$this->atts = $atts;
$elem = $this->getElementHolder( $width );
if ( isset( $this->settings['custom_markup'] ) && '' !== $this->settings['custom_markup'] ) {
$markup = $this->settings['custom_markup'];
$elem = str_ireplace( '%wpb_element_content%', $this->customMarkup( $markup, $content ), $elem );
$output .= $elem;
} else {
$inner = $this->outputTitle( $this->settings['name'] );
$inner .= $this->paramsHtmlHolders( $atts );
$elem = str_ireplace( '%wpb_element_content%', $inner, $elem );
$output .= $elem;
}
return $output;
}
}
}
I follow this Git example to create my custom elements : https://gist.github.com/Webcreations907/ff0b068c5c364f63e45d

Woocommerce add img tag on order admin details page

I have a wordpress website where customers make an image with text and icons, once processed thru woocommerce and payed for that image name 12345.png is saved as Customer_product_image
function add_order_item_meta($item_id, $values) {
$key = 'customer_product_image'; // Define your key here
$value = $values['user_img']; // Get your value here
woocommerce_add_order_item_meta($item_id, $key, $value);
}
And i works great, but now i'm banning my head against the wall! When the purchased image is displayed on the Order admin detail page, it shows up as CUSTOMER_PRODUCT_IMAGE: 1234.png how on earth would i go about wrapping that within an image tag so the image is displayed there?
I've searched high and low on google but haven't been able to find anything, its probably that i dont know what do actually search for....
This did the trick for me!
First i added this snippet for removing the custom meta item on order detail render:
add_filter( 'woocommerce_hidden_order_itemmeta', 'hide_order_item_meta_fields' );
function hide_order_item_meta_fields( $fields ) {
$fields[] = 'current_view';
$fields[] = 'custom_image';//Add all meta keys to this array,so that it will not be displayed in order meta box
return $fields;
}
second i added it back with this, and with the desired text and image tag:
add_action( 'woocommerce_after_order_itemmeta', 'order_meta_customized_display',10, 3 );
function order_meta_customized_display( $item_id, $item, $product ){
$all_meta_data=get_metadata( 'order_item', $item_id, "", "");
$useless = array(
"_qty","_tax_class","_variation_id","_product_id","_line_subtotal","_line_total","_line_subtotal_tax","_line_tax","_line_tax_data"
);// Add key values that you want to ignore
$customized_array= array();
foreach($all_meta_data as $data_meta_key => $value) {
if(!in_array($data_meta_key,$useless)){
$newKey = ucwords(str_replace('_'," ",$data_meta_key ));//To remove underscrore and capitalize
$customized_array[$newKey]=ucwords(str_replace('_'," ",$value[0])); // Pushing each value to the new array
}
}
if (!empty($customized_array)) {
foreach($customized_array as $data_meta_key => $value){
echo "<div class='product_container'><span>Produkt Billede: </span><img src='".wp_upload_dir()['baseurl'].'/flapper/'. $value ."' /> </div>";
}
}
}
i found the answer to this question on this page
You can use the filter woocommerce_order_item_display_meta_value to output the image. Place this code in your functions.php file, you'll need to modify the src attribute of the img tag to include the appropriate URL before the filename value. You can also modify the display label with the filter woocommerce_order_item_display_meta_key
add_filter( 'woocommerce_order_item_display_meta_value', 'modify_order_item_display_value' , 10, 3 );
function modify_order_item_display_value( $display_value, $meta, $wc_order_item ) {
$meta_data = $meta->get_data();
if( $meta_data['key'] === 'customer_product_image' ) {
return '<img src="' . $meta_data['value'] . '">';
}
return $display_value;
}
add_filter('woocommerce_order_item_display_meta_key', 'modify_order_item_display_key', 10, 3);
function modify_order_item_display_key( $display_key, $meta, $wc_order_item ) {
$meta_data = $meta->get_data();
if( $meta_data['key'] === 'customer_product_image' ) {
return 'Customer Image';
}
return $display_key;
}

Apply the_title filter to post titles AND backend auto social-sharing plugin, but not nav menu

The Challenge
I have been trying to write a filter that applies (prepends a string) to (a) post titles of a certain post type and (b) to that same post type WordPress admin backend for use by an auto social sharing plugin.
I've gotten really close. I've achieved:
1. prepending only to custom post_type post titles
2. prepending to custom_post type titles AND all nav menu items
3. prepending to ALL titles (post, page, nav menu, and backend auto-sharing plugin)
The final, 3rd attempt got me very hopeful, but yet still, no matter the function, conditional statement, or combination I use, I can't get custom_post title AND backend social sharing plugin, but not nav menu items.
My Code - what I've tried
I've tried different conditionals, like is_admin() and !is_nav_menu with all nav menu IDs. I've also tried comparing the ID of the post to the ID of the menu. Finally, I've also tried adding the filter only to loop_start and that seems promising if combined with other statements. I will clean up my code for your review but I leave it this way for now in hopes it helps to see what I've tried and maybe where I went wrong with any of those methods.
// , $id = NULL
function append_album_review_to_title( $title ) {
global $post;
global $dont_apply_title_filter;
$text = 'Album Review: ';
$prepended_title = $text . $title;
/*
$nav_menus_obj = wp_get_nav_menus();
$nav_menu_ids = '';
foreach( $nav_menus_obj as $nav_menu ) {
$nav_menu_ids .= $nav_menu->term_id . ',';
}
rtrim($nav_menu_ids, ',');
*/
// if ( get_post_type( $post->ID ) == 'album_review' && in_the_loop() ){
// if ( get_post_type( $post->ID ) == 'album_review' && $id == get_the_ID() ){
// if ( !is_nav_menu( '32057,32058,35135,32054,32056' ) ) {
// if ( get_post_type( $post->ID ) == 'album_review' && is_nav_menu( $id ) ) {
if ( get_post_type( $post->ID ) == 'album_review' && !$dont_apply_title_filter ) {
//print_r( $nav_menu_ids );
//print_r( is_nav_menu( $nav_menu_ids ) );
return $prepended_title;
/*
} elseif ( get_post_type( $post->ID ) == 'album_review' ) {
return $title;
*/
} else {
return $title;
};
}
add_filter('the_title', 'append_album_review_to_title');
//add_action('save_post', 'custom_post_type_title', 100);
/*
function set_custom_title() {
add_filter( 'the_title', 'append_album_review_to_title', 10, 2 );
}
add_action( 'loop_start', 'set_custom_title' );
*/
Solution
function append_album_review_to_title( $title, $id = NULL ) {
if ($id) {
if ( get_post_type( $id ) == 'album_review' ){
return 'Album Review: ' . $title;
} else {
return $title;
}
} else {
return 'Album Review: ' . $title;
};
}
add_filter('the_title', 'append_album_review_to_title', 10, 2);
Explanation
First trying this:
function append_album_review_to_title( $title, $id ) {
if ( get_post_type( $id ) == 'album_review' ){
return 'Album Review: ' . $title;
} else {
return $title;
}
}
add_filter('the_title', 'append_album_review_to_title', 10, 2);
I noticed that on the backend, the social sharing plugin I am using to auto-share posts would return warnings like missing parameter 2 and Notice: Trying to get property of non-object..., and so it occurred to me that unlike the front end (nav menu and the loop), in the backend, apparently an $id is not being passed to the_title and so I can use this as a conditional.
Checking for $id will, for my purposes, tell me that if it is true, we are on front end, if it is false, then we are at backend post view.
This code accomplishes what I need (ie, modify post title in the loop, do NOT modify nav menu items, and modify the_title for use by a backend social sharing plugin):

Resources