Sanitize and combine four ACF fields into one new ACF field - php

I'm combining four individual ACF fields into one text field, but I need to sanitize those entries individually so I can use them all as CSS classes. I've been trying to use sanitize_title() to turn plain english field values into the equivalent of post slugs—lowercase with dashes instead of spaces and no special characters.
A simplified, two-field example would be:
Status One / Status Two and Sector One / Sector Two / Sector Three
into
status-one-status-two sector-one-sector-two-sector-three
Note that I'm trying to sanitize before merging so I can keep my entries separated by spaces (i.e. multiple CSS classes instead of one-incredibly-long-css-class.
I found the original function here and successfully modified it to merge four of my fields into a fifth ACF field.
function my_acf_save_post( $post_id ) {
$value1 = get_field( 'status', $post_id );
$value2 = get_field( 'sector', $post_id );
$value3 = get_field( 'subsector', $post_id );
$value4 = get_field( 'lead', $post_id );
$clean1 = sanitize_title( $value1 );
$clean2 = sanitize_title( $value2 );
$clean3 = sanitize_title( $value3 );
$clean4 = sanitize_title( $value4 );
$merge = implode(" ",$clean1).' '.implode(" ",$clean2).' '.implode(" ",$clean3).' '.implode(" ",$clean4);
update_field( 'css_classes', $merge );
}
add_action('acf/save_post', 'my_acf_save_post', 20);
I've tried a number of sanitize_* functions—most of them at this point—and a kajillion different syntaxes and I still can't get it to work because I'm a designer who writes a bit of code and not a developer (but you know that by now). kthxbai in advance.

Try out this code to create a slug and merge fields into one.
function custom_slug($text, string $divider = '-')
{
// replace non letter or digits by divider
$text = preg_replace('~[^\pL\d]+~u', $divider, $text);
// transliterate
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
// remove unwanted characters
$text = preg_replace('~[^-\w]+~', '', $text);
// trim
$text = trim($text, $divider);
// remove duplicate divider
$text = preg_replace('~-+~', $divider, $text);
// lowercase
$text = strtolower($text);
if (empty($text)) {
return 'n-a';
}
return $text;
}
function my_acf_save_post( $post_id ) {
$value1 = get_field( 'status', $post_id );
$value2 = get_field( 'sector', $post_id );
$value3 = get_field( 'subsector', $post_id );
$value4 = get_field( 'lead', $post_id );
$clean1 = custom_slug( $value1 );
$clean2 = custom_slug( $value2 );
$clean3 = custom_slug( $value3 );
$clean4 = custom_slug( $value4 );
$merge = $clean1.' '.$clean2.' '.$clean3.' '.$clean4;
update_field( 'css_classes', $merge, $post_id );
}
add_action('acf/save_post', 'my_acf_save_post', 20);

Related

Replacing characters at start/end of string with PHP's ltrim/rtrim

I am looking to clean whitespace and hyphen characters from the start/end of a string.
I have the following test code:
$sample_titles = array(
'— — -Daisy Chain –',
'Ocarina for a Mermaid –',
' –—Another for a Sailor - '
);
foreach ( $sample_titles as $title ) {
$updated_title = ltrim( $title, ' -–—' );
$updated_title = rtrim( $updated_title, ' -–—' );
echo $updated_title . '<br/>';
}
This correctly outputs:
Daisy Chain
Ocarina for a Mermaid
Another for a Sailor
However, when I apply the same ltrim/rtrim logic in a foreach loop (over post titles, I'm "cleaning" imported data, the rest of the code is irrelevant) like this:
foreach ( $product_ids as $key => $product_id ) {
$title = get_the_title( $product_id );
$updated_title = ltrim( $title, ' -–—' );
$updated_title = rtrim( $updated_title, ' -–—' );
echo $updated_title . '<br/>';
};
I still end up with the hyphens/dashes/whitespace like this:
Orb 3 –
Ocarina for a Mermaid –
Mini Marina –
Any ideas why this works in one context but not the other?
Simply wrapping the title grab function in html_entity_decode() fixed the issue I was having:
$product_title = html_entity_decode( get_the_title( $product_id ) );

Add the product name to the email subject in WooCommerce

How can I add the product name in the subject line of this email. is there a list of the available filters for the email subject lines? I'd like to be able to do this without having to write custom php. I'm looking to do something like this:
Completed Order - [{product_name}] ({order_number}) - {order_date}
Below code tried and added in functions.php but subject is not displaying:
add_filter( 'woocommerce_email_subject_customer_completed_order', 'customer_completed_order_subject', 10, 2 );
function customer_completed_order_subject($string,$order){
$fname = $order->billing_first_name;
$lanme = $order->billing_last_name;
$email = $order->billing_email;
$items = $order->get_items();
foreach($items as $meta_key => $items){
$product_name = $items['name'];
$product_id = $items['product_id'];
}
$subject_line = get_field('email_subject',$product_id);
$subject_line = str_replace('{product}',$product_name,$subject_line);
$subject_line = str_replace('{biller_fname}',$fname,$subject_line);
$subject_line = str_replace('{biller_lastname}',$lanme,$subject_line);
$subject_line = str_replace('{biller_email}',$email,$subject_line);
$subject_line = str_replace('{blog_name}',get_bloginfo('name'),$subject_line);
return $subject_line;
}
You are not using the correct hook and there are mistakes… Instead use the following:
add_filter( 'woocommerce_email_format_string' , 'add_custom_email_format_string', 10, 2 );
function add_custom_email_format_string( $string, $email ) {
$order = $email->object; // Get the instance of the WC_Order OBJECT
$order_items = $order->get_items(); // Get Order items
$order_item = reset($order_items); // Get the irst order item
// Replace placeholders with their respective values
$string = str_replace( '{biller_fname}', $order->billing_first_name(), $string );
$string = str_replace( '{biller_lname}', $order->billing_last_name(), $string );
$string = str_replace( '{biller_email}', $order->billing_email(), $string );
$string = str_replace( '{product_name}', $order_item->get_name(), $string );
$string = str_replace( '{blog_name}', get_bloginfo('name'), $string );
return $string;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Note: An order can have many items, so many product names. In the code above, I only keep the first product name…
If you want to handle multiple product names, you will use:
add_filter( 'woocommerce_email_format_string' , 'add_custom_email_format_string', 10, 2 );
function add_custom_email_format_string( $string, $email ) {
$order = $email->object; // Get the instance of the WC_Order OBJECT
$products_names = array();
// Loop through order items
foreach( $order->get_items() as $item ) {
$products_names[] = $item->get_name();
};
// Replace placeholders with their respective values
$string = str_replace( '{biller_fname}', $order->billing_first_name(), $string );
$string = str_replace( '{biller_lname}', $order->billing_last_name(), $string );
$string = str_replace( '{biller_email}', $order->billing_email(), $string );
$string = str_replace( '{product_name}', implode(' ', $products_names), $string );
$string = str_replace( '{blog_name}', get_bloginfo('name'), $string );
return $string;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Related: Add a custom placeholder to email subject in WooCommerce

Modify wp_trim_words function to return content split in 2

Im trying to modify the wp_trim_words function to return the left over words as well as the first part, any help much appreciated.
function wp_trim_words_new( $text, $num_words = 55, $more = null ) {
if ( null === $more )
$more = __( '…' );
$original_text = $text;
$text = wp_strip_all_tags( $text );
/* translators: If your word count is based on single characters (East Asian characters),
enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
$text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
preg_match_all( '/./u', $text, $words_array );
$words_array = array_slice( $words_array[0], 0, $num_words + 1 );
$sep = '';
} else {
$words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
$sep = ' ';
}
if ( count( $words_array ) > $num_words ) {
array_pop( $words_array );
$text = implode( $sep, $words_array );
$text = $text . $more;
} else {
$text = implode( $sep, $words_array );
}
/**
* Filter the text content after words have been trimmed.
*
* #since 3.3.0
*
* #param string $text The trimmed text.
* #param int $num_words The number of words to trim the text to. Default 5.
* #param string $more An optional string to append to the end of the trimmed text, e.g. ….
* #param string $original_text The text before it was trimmed.
*/
return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text );
}
It seems to me that you simply copied the trim_words() function of Wordpress. You could have done better. Your desired output is unclear.
Supposing that you would be happy with the simple tag-less string of left-over words returned together with the output of the original function, you could use something like this (untested):
function wp_trim_words_2( $text, $num_words = 55, $more = null ) {
if ( null === $more )
$more = __( '…' );
$original_text = $text;
$text = wp_strip_all_tags( $text );
/* translators: If your word count is based on single characters (East Asian characters),
enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
$text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
preg_match_all( '/\X/u', $text, $match_array );
$split_array = $match_array[0];
$sep = '';
} else {
$split_array = preg_split( "/[\n\r\t ]+/", $text, -1, PREG_SPLIT_NO_EMPTY );
$sep = ' ';
}
$words_array = array_slice( $split_array, 0, $num_words + 1 );
if ( count( $words_array ) > $num_words ) {
array_pop( $words_array );
$text = implode( $sep, $words_array );
$text = $text . $more;
$rest = implode( $sep, array_slice( $split_array, $num_words ) );
} else {
$text = implode( $sep, $words_array );
$rest = '';
}
/**
* Filter the text content after words have been trimmed.
*
* #since 3.3.0
*
* #param string $text The trimmed text.
* #param int $num_words The number of words to trim the text to. Default 5.
* #param string $more An optional string to append to the end of the trimmed text, e.g. ….
* #param string $original_text The text before it was trimmed.
*/
return array( apply_filters( 'wp_trim_words_2', $text, $num_words, $more, $original_text ), $rest );
}
to be called like this:
list($trimmed, $rest) = wp_trim_words_2($text);
I'm not sure about apply_filters(), though. Should it be passed the $rest too? It's too time-consuming for me to study this one, sorry.
If, on the other hand, you wanted the $rest before tag removal (i.e., before the call to wp_strip_all_tags()), then things are much more complicated, because the original functions splits the input into words after that, and I can think only of inefficient ways to keep the correspondence between the input and the output of wp_strip_all_tags().
BTW, the original code by WordPress contains a bug: words for East Asian languages are wrongly counted (and potentially split!) as single Unicode characters instead of Unicode graphemes. The fix is to use '/\X/u' instead of '/./u' (read here for an explanation).
Not sure you need to tweak that function at all, this code does what you ask:
$trimmed_text = wp_trim_words($text, $wordlength, '');
// Measure full and trimmed widths for comparison
$fw = mb_strwidth($text);
$tw = mb_strwidth($trimmed_text);
if( $fw != $tw ){
$clipped_text = mb_strimwidth($text, $tw, $fw - $tw, '');
// Text has been cut
} else {
// Text has NOT been cut
}
or wrap it in a function behaving as you wanted initially
function wp_trim_words_new($text, $length, $delimiter=''){
$trimmed_text = wp_trim_words($text, $length, $delimiter);
$fw = mb_strwidth($text);
$tw = mb_strwidth($trimmed_text);
if( $fw != $tw ){
return [$trimmed_text, mb_strimwidth($text, $tw, $fw - $tw, $delimiter)];
} else {
return [$text, null];
}
}
You can accomplish this without the wp_trim_words function.
$sentence = "This is a sample sentence";
$words = explode(" ", $sentence);
$half = count($words) / 2;
$first_half = implode(" ", array_slice($words, 0, $half));
$second_half = implode(" ", array_slice($words, $half));
echo $first_half . "\n";
echo $second_half;

Retain formatting while using excerpt [ wp_trim_words() ]

I am using <?php echo wp_trim_words(get_the_content(), 100); ?> in my template file to control amount of words to be displayed on a page and also have a link below it for taking user to the next page to read entire content but this function removes all the formatting of content on preview page.
I can't use wordpress default excerpt function here as it is being used elsewhere and i need this to be of different length than that. Is there a way to retain formatting while using this ?
Thanks
I found a solution to this may be it can help others as well.
function content_excerpt($excerpt_length = 5, $id = false, $echo = true) {
$text = '';
if($id) {
$the_post = & get_post( $my_id = $id );
$text = ($the_post->post_excerpt) ? $the_post->post_excerpt : $the_post->post_content;
} else {
global $post;
$text = ($post->post_excerpt) ? $post->post_excerpt : get_the_content('');
}
$text = strip_shortcodes( $text );
$text = apply_filters('the_content', $text);
$text = str_replace(']]>', ']]>', $text);
$words = preg_split("/[\n\r\t ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
if ( count($words) > $excerpt_length ) {
array_pop($words);
$text = implode(' ', $words);
$text = $text . $excerpt_more;
} else {
$text = implode(' ', $words);
}
if($echo)
echo apply_filters('the_content', $text);
else
return $text;
}
function get_content_excerpt($excerpt_length = 5, $id = false, $echo = false) {
return content_excerpt($excerpt_length, $id, $echo);
}
// Call function in template file via
<?php content_excerpt(50); // 50 is amount to words ?>
To anyone else facing similar issues, I took the default wp_trim_words() function directly out of core, and altered it to not strip tags:
function wp_trim_words_retain_formatting( $text, $num_words = 55, $more = null ) {
if ( null === $more )
$more = __( '…' );
$original_text = $text;
/* translators: If your word count is based on single characters (East Asian characters),
enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
$text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
preg_match_all( '/./u', $text, $words_array );
$words_array = array_slice( $words_array[0], 0, $num_words + 1 );
$sep = '';
} else {
$words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
$sep = ' ';
}
if ( count( $words_array ) > $num_words ) {
array_pop( $words_array );
$text = implode( $sep, $words_array );
$text = $text . $more;
} else {
$text = implode( $sep, $words_array );
}
/**
* Filter the text content after words have been trimmed.
*
* #since 3.3.0
*
* #param string $text The trimmed text.
* #param int $num_words The number of words to trim the text to. Default 5.
* #param string $more An optional string to append to the end of the trimmed text, e.g. ….
* #param string $original_text The text before it was trimmed.
*/
return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text );
}
Try:
echo apply_filters( 'the_content', wp_trim_words( get_the_content(), 100 ) );

PHP over trim! Text disappearing! But why?

I just want to display the categories for my blog posts, but to SOME of the categories (and especially if they stand alone, the last bit get's trimmed away ~ "Music" becomes "Mu", and "Adventure" becomes "Adventur" ... any help? Please!
// Category boxes :P
function showcatz() {
global $post;
echo '<div class="categz_wrapper"><div class="categz">';
// get the category IDs assigned to post
$categories = wp_get_post_categories( $post->ID, array( 'fields' => 'ids' ) );
// separator between links
$separator = '</div><div class="categz"> ';
if ( $categories ) {
// List categories
$cat_ids = implode( ',' , $categories );
// Remove ONE category from the list
$kill = array("411,", "411");
$killit = str_replace($kill, "", $cat_ids);
$cats = wp_list_categories( 'title_li=&style=none&echo=0&include=' . $killit);
$cats = rtrim( trim( str_replace( '<br />', $separator, $cats ) ), $separator );
// Only show categories if there is any
if ( $killit ) { echo $cats; }
}
echo '</div></div>';
}
your passing a parameter to rtrim called $separator which has the value </div><div class="categz"> so when the following statement is executed it will remove the following chars from your string. div<>clastegz
rtrim( str_replace( '<br />', $separator, $cats ) ), $separator );
Solution, remove the second parameter to rtrim

Categories