So... I'm developing a wordpress plugin and it is suppose to change source of images that you get from wordpress function get_the_post_thumbnail_url()
You have a theme and there you get sources for images with get_the_post_thumbnail_url($id) and it returns something like this https://wordpress/path/to/image.jpg and I want to change the output of that function INSIDE that plugin, like when I modify it in my plugin and you use that function inside your theme it outputs my modified version.
I tried using add_filter() and it just doesn't work and I don't know if it's even supposed to work.
function my_function_idk( $example ) {
$asd = $example.'lul123';
return $asd;
}
add_filter( 'get_the_post_thumbnail_url', 'my_function_idk' );
// expected output: https://wordpress/path/to/image.jpglul123
Actual output: https://wordpress/path/to/image.jpg
Maybe I understood that you have to use apply_filters() to make that filter work, but you need to use it in THAT THEME and I don't want that. I just want to modify that function... Is it possible?
I just don't understand their documentation... I'm raging over wordpress... fkn bulsht.
To use hook like add_filter( 'get_the_post_thumbnail_url_hook', 'my_function_idk' );. Inside the function that you want to hooked needed to that:
function get_the_post_thumbnail_url( $post = null, $size = 'post-thumbnail' ) {
$post_thumbnail_id = get_post_thumbnail_id( $post );
if ( ! $post_thumbnail_id ) {
return false;
}
return apply_filters('get_the_post_thumbnail_url_hook', $post_thumbnail_id, $size );
}
Look at return apply_filters('get_the_post_thumbnail_url_hook', $post_thumbnail_id, $size );
And this function does not have hook. Look it inside core. So you can't "hooked" it.
But you have one option:
Inside get_the_post_thumbnail_url we got wp_get_attachment_image_url that have wp_get_attachment_image_src And it function have return apply_filters( 'wp_get_attachment_image_src', $image, $attachment_id, $size, $icon );
So you can through the function queue connect to the one at the end of the chain.
Try that:
add_filter( 'wp_get_attachment_image_src', 'my_function_idk', 90, 4 );. Documentation
In the order email templates (for example email-order-items.php), WooCommerce uses the function wc_display_item_meta to display product details in the order table. The function code is present in the wc-template-functions.php file (line number 3011). I am copying the function code below for reference
function wc_display_item_meta( $item, $args = array() ) {
$strings = array();
$html = '';
$args = wp_parse_args( $args, array(
'before' => '<ul class="wc-item-meta"><li>',
'after' => '</li></ul>',
'separator' => '</li><li>',
'echo' => true,
'autop' => false,
) );
foreach ( $item->get_formatted_meta_data() as $meta_id => $meta ) {
$value = $args['autop'] ? wp_kses_post( $meta->display_value ) : wp_kses_post( make_clickable( trim( $meta->display_value ) ) );
$strings[] = '<strong class="wc-item-meta-label">' . wp_kses_post( $meta->display_key ) . ':</strong> ' . $value;
}
if ( $strings ) {
$html = $args['before'] . implode( $args['separator'], $strings ) . $args['after'];
}
$html = apply_filters( 'woocommerce_display_item_meta', $html, $item, $args );
if ( $args['echo'] ) {
echo $html; // WPCS: XSS ok.
} else {
return $html;
}
}
The problem is: it doesn't take any arguments that can help me filter out item data that I don't want to show in the order email. I don't want to change this function in the wc-template-functions.php as it's a core file. So, I want to know if there's a piece of code that I can add to functions.php that'll somehow modify this wc_display_item_meta function to filter out specific item meta.
Note: I know someone might suggest why not just remove that particular item data from the product details, but that data is essential to internal order processing. I just don't want it to show to the customers.
Update #1: What meta data I don't want to show in the order email? Below is a screenshot of an order email. I have highlighted three item data.."Qty Selector", "Qty" and "Total". I want all these three to not show in the order email.
Try the following without any guarantee (as I don't really have the real necessary keys):
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'unset_specific_order_item_meta_data', 10, 2);
function unset_specific_order_item_meta_data($formatted_meta, $item){
// Only on emails notifications
if( is_admin() || is_wc_endpoint_url() )
return $formatted_meta;
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array('Qty Selector', 'Qty', 'Total') ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
Code goes in function.php file of your active child theme (active theme). Tested with other meta data than yours and works. I hope it will work for you too.
Now, the hook used with this code is the right filter hook. It's located in the WC_Order_Item method get_formatted_meta_data() and allows to filter the order item meta data.
There is a bug with the accepted answer, and all of the other snippets that I've found around the internet, so I'm posting my own answer here in the hopes that stores around the world don't accidentally leak information.
The problem is that when you use the Order actions meta box to resend the email, the filter check fails because is_admin() === true.
The order actions is a meta box down the side of the Orders page:
So the first time, when the order is created, it filters the email like you want, but then if an admin resends the email to a customer then it will be broken and show all of the meta fields to the user in the resent email.
The code that fixes this scenario is this:
$is_resend = isset($_POST['wc_order_action']) ? wc_clean( wp_unslash( $_POST['wc_order_action'] ) ) === 'send_order_details' : false;
if ( !$is_resend && (is_admin() || is_wc_endpoint_url() ) ) {
return $formatted_meta;
}
So if you look at the linked snippet then you will see the meta box adds that field to the $_POST. It has to be cleaned up like that as well or it won't match.
The full example integrated into the accepted solution's answer is:
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'unset_specific_order_item_meta_data', 10, 2);
function unset_specific_order_item_meta_data($formatted_meta, $item){
// Only on emails notifications
$is_resend = isset($_POST['wc_order_action']) ? wc_clean( wp_unslash( $_POST['wc_order_action'] ) ) === 'send_order_details' : false;
if ( !$is_resend && (is_admin() || is_wc_endpoint_url() ) ) {
return $formatted_meta;
}
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array('Qty Selector', 'Qty', 'Total') ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
I hear, that you want to show the Order Item Meta Data in the admin backend only. That's actually a tricky one. I have played around for some hours but no solution I have found, guarentee that the Order Itema Meta Data doesn't show up in e-mails to the customer.
The thing is that there are several ways these e-mails are fired (eg. through the resend meta box (which #rtpHarry mentions) or by changing order status either at the order overview, the single order view or an automatic/programmatically order status change). That gives many cases where it's neccessary to unset the Order Item Meta Data - you need to find all cases except the admin backend.
Therefore, my suggestion is to first completely remove the Order Item Meta Data using the above mentioned woocommerce_order_item_get_formatted_meta_data filter and then add them again using an action like woocommerce_before_order_itemmeta which ONLY fires in the admin backend. Because the Order Item Meta Data is unset you cannot use the get_formatted_meta_data method to get the data. Instead you can use the function wc_get_order_item_meta.
Complete code (tested and works):
//Hide 'Qty Selector', 'Qty' and 'Total' completely
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'unset_specific_order_item_meta_data');
function unset_specific_order_item_meta_data($formatted_meta){
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array('Qty Selector', 'Qty', 'Total') ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
//Add 'Qty Selector', 'Qty' and 'Total' in the admin backend only
add_action('woocommerce_before_order_itemmeta', 'add_specific_order_item_meta_data_in_backend', 10, 2);
function add_specific_order_item_meta_data_in_backend( $item_id, $item ) {
//Only applies for line items
if( $item->get_type() !== 'line_item' ) return;
$qty_sel_lines = wc_get_order_item_meta($item_id, 'Qty Selector', false);
$qty_lines = wc_get_order_item_meta($item_id, 'Qty', false);
$total_lines = wc_get_order_item_meta($item_id, 'Total', false);
foreach ($qty_sel_lines as $qty_sel_line){
echo $qty_sel_line . '<br>';
}
foreach ($qty_lines as $qty_line){
echo $qty_line . '<br>';
}
foreach ($total_lines as $total_line){
echo $total_line. '<br>';
}
}
Note:
If you need to add the Order Item Meta Data to the admin e-mails, you need to do that seperately. I have not examined the options on that.
I kind of agreed with #pstidsen argument. So I was thinking about how to solve this without to re-add all the metadata, since it kind of disturbed me not to handle it in the same way as it was added before. I have additional filters to add css classes and so on to the metadata. So there would've been a need to take care of.
So here is my approach which gives you the opportunity to use it for emails, custom emails, pdf invoices or similar scenarios. I also uses a fallback to filter for our frontend or any situation we didn't consider.
Please keep the order of if else in mind. I checked the admin filter the last, to make sure any other filter gets fired before. The situation for an email at example is: It's sent from the admin interface, so the admin filter is true but also is the email filter.
Functionality for a different filter for admin emails is given as well.
/**
* This function filters all unwanted item metadata, if the specific filter are hooked in
* we also use a fallback filter, if none of the hooks are fired
*
* #params array() $metadata
*
*/
add_filter( 'woocommerce_order_item_get_formatted_meta_data', 'custom_filter_item_meta_data', 50, 1);
function custom_filter_item_meta_data( $metadata ){
if ( empty( $metadata ) ) return $metadata;
$filter_array = array();
if ( apply_filters( 'custom_filter_item_meta_email', false ) ){
// email filter goes here
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'email' );
}elseif ( apply_filters( 'custom_filter_item_meta_admin_email', false ) ){
// admin email filter goes here
// pass
elseif ( apply_filters( 'custom_filter_item_meta_invoice', false ) ){
// invoice filter goes here
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'invoices' );
}elseif ( apply_filters( 'custom_filter_item_meta_admin', false ) ){
// general admin filter goes here
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'admin_backend' );
}else{
// fallback filter
$filter_array = array( 'whatever','you', 'wanna', 'filter, 'for', 'fallback' );
}
foreach ( $metadata as $key => $meta ){
if ( in_array( $meta->key, $filter_array ) ){
unset ( $metadata[ $key ] );
}
}
return $metadata;
}
/**
* Is used to enable our item meta filter for our admin backend
* Hooked:
* #admin_init
*/
add_action( 'admin_init', 'custom_init_item_meta_filter_admin', 50, 1 );
function custom_init_item_meta_filter_admin(){
add_filter( 'custom_filter_item_meta_admin', function(){ return true; });
}
/**
* Is used to enable our item meta filter for emails
* Hooked:
* #woocommerce_email_order_details
*/
add_action( 'woocommerce_email_order_details', 'custom_init_item_meta_filter_email' ), 10, 2);
function custom_init_item_meta_filter_email( $order, $sent_to_admin ){
if ( $sent_to_admin ){
add_filter('custom_filter_item_meta_admin_email', function(){ return true; } );
}else{
add_filter('custom_filter_item_meta_email', function(){ return true; } );
}
}
/**
* Is used to enable our item meta filter for invoices
* Hooked:
* #wpo_wcpdf_before_order_details
*/
add_filter( 'wpo_wcpdf_before_order_details', 'custom_init_item_meta_filter_invoice', 10, 1);
function custom_init_item_meta_filter_invoice(){
add_filter( 'custom_filter_item_meta_invoice', function(){ return true; });
}
I didn't test it in that "flattened" format. I used it within different classes of my oop coded plugin and edited it to post it here.
Structure of the html code of the metadata tags
If I want to delete meta_data 2 and meta_data 3:
add_filter( 'woocommerce_display_item_meta', 'filter_woocommerce_display_item_meta', 10, 3 );
function filter_woocommerce_display_item_meta( $html, $item, $args ) {
$arrayPortionsTags = explode("<li", $html);
unset($arrayPortionsTags[2],$arrayPortionsTags[3]);
$firstLi = array( '<li' );
$lastUl = array( '</ul>' );
array_splice( $arrayPortionsTags, 1, 0, $firstLi );
array_splice( $arrayPortionsTags, 3, 0, $lastUl );
$html= implode('',$arrayPortionsTags);
return $html;
};
I try to add my products with as little human input as possible.
Therefor I'm looking for a solution to grab the title tag which is in my added product image and put it in the Product name field on or before saving the product. Any attempts to achieve this are failing because WordPress "thinks" that no title is given (so no slug could be generated). At least I think that this is the case.
See screenshot of the field
I tried to use a code snippet I found here on SO and to rework it to a working solution but I fail to get it right.
Here is the code I came up with:
function fcsp_set_title_on_save( $post_id ) {
$post_thumbnail_id = get_post_thumbnail_id( $post_id );
$filemeta = wp_get_attachment_metadata( $post_thumbnail_id, FALSE );
// Set this variable to false initially.
static $updated = false;
// If title has already been set once, bail.
if ( $updated ) {
return;
}
// Since we're updating this post's title, set this
// variable to true to ensure it doesn't happen again.
$updated = true;
$title = $filemeta['image_meta']['title'];
// Update the post's title.
wp_update_post( [
'ID' => $post_id,
'post_title' => $title,
] );
}
add_action( 'save_post', 'fcsp_set_title_on_save' );
Any idea how to accomplish this?
Please put this code in function.php if this is woocommerce you are using (I think rather than custom post type )
if(class_exists('WC_Admin_Meta_Boxes')) {
class wcsave extends WC_Admin_Meta_Boxes {
public function __construct() {
add_action( 'save_post', array( $this, 'save_meta_boxes' ), 1, 2 );
add_action( 'woocommerce_process_product_meta', 'WC_Meta_Box_Product_Data::save', 10, 2 );
}
public function save_meta_boxes( $post_id, $post ) {
//$_POST enter your post data here, this will help to control the post request from product woocommerce
//if product is updating don't execute image title code section
if(!empty($post->post_title)) {
return;
}
//if new product is being added.
if(!empty($_POST['post_ID']) && $post_id == $_POST['post_ID']) {
$post_thumbnail_id = get_post_thumbnail_id( $post_id );
$attachment_data = get_post( $post_thumbnail_id,OBJECT );
$title = count($attachment_data) > 0 ? $attachment_data->post_title : "PRODUCT-".$post_id;
remove_action( 'save_post', array( $this, 'save_meta_boxes' ) , 1, 2 );
wp_update_post( [
'ID' => $post_id,
'post_title' => $title,
'post_status' => $post->post_status
] );
// re-hook this function
add_action( 'save_post', array( $this, 'save_meta_boxes' ) , 1, 2 );
}
}
}
new wcsave();
}
But please note, as I've tested you need at least some other info along with product image you are uploading like product description, rest it will save the image name as product title.
Thanks
I'm struggling with one thing. I've got such wordpress function:
function wpq_insert_attachment_data($data, $postarr){
if (!is_single() ) {
$posttitle = get_the_title( $postarr['post_parent'] );
$data['post_title'] = $posttitle;
$data['post_name'] = $posttitle;
return $data;
}}
add_filter( 'wp_insert_attachment_data', 'wpq_insert_attachment_data', 10, 2 );
It works superb but it covers all single/custom post type/pages etc. Is there any way to EXCLUDE pages from that function? I've tried sorting it out with is_single() yet without success.
Use is_singular in your statement to target specific post types.
Can also do an array to include or exclude.
If (! is_singular()) ..... or.....
(! is_singular(array('page','food',foo')))
Then it will only run on the singles for whichever post type you're targeting.
Looks like you just need to tweak your conditional statement:
if (!is_single() ) {
Should become:
if (!is_page() ) {
Load the function only on specific page, add your page id in is_page(id_here) :
function wpq_insert_attachment_data($data, $postarr){
if ( is_page(page_id)){
$posttitle = get_the_title( $postarr['post_parent'] );
$data['post_title'] = $posttitle;
$data['post_name'] = $posttitle;
return $data;
}
}
add_filter( 'wp_insert_attachment_data', 'wpq_insert_attachment_data', 10, 2 );
you can also add your page slug instead of id like :
if ( is_page('slug'))
Or exclude page(s)
if ( !is_page('slug'))
if ( !is_page(array('slug-1', 'slug-2') )
i have a very small problem. I hope someone could enlighten me why this is giving me this error. I have here my function that displays the url of the image of one of my category under my custom taxonomy called 'item_category'. this is under functions.php
function list_all_categories(){
$categories = get_term( 2 ,'item_category' );
$src = get_term_meta(2,'javo_item_category_featured',true);
echo $src;
}
add_shortcode( 'list','list_all_categories' );
You must update your Wordpress to have access to the get_term_meta function since you're runing version 4.2.6 but it's released on 4.4.0.
You can see it in the documentation here:
https://developer.wordpress.org/reference/functions/get_term_meta/
If you don't want to update for whatever reason, the source of the function:
function get_term_meta( $term_id, $key = '', $single = false ) {
// Bail if term meta table is not installed.
if ( get_option( 'db_version' ) < 34370 ) {
return false;
}
return get_metadata( 'term', $term_id, $key, $single );
}