the code:
add_filter( 'the_title', 'shorten_woo_product_title', 10, 2 );
function shorten_woo_product_title( $title, $id ) {
if ( ! is_singular( array( 'product' ) ) && get_post_type( $id ) === 'product' && strlen( $title ) > 30 ) {
return substr( $title, 0, 30) . '…'; // change last number to the number of characters you want
} else {
return $title;
}
}
I know I should add wp_is_mobile() somewhere but don't know where should I add it exactly.
If you want to use wp_is_mobile() then it just returns a boolean, so you could use it anywhere that wraps the output:
add_filter( 'the_title', 'shorten_woo_product_title', 10, 2 );
function shorten_woo_product_title( $title, $id ) {
if ( wp_is_mobile() ) {
if ( ! is_singular( array( 'product' ) ) && get_post_type( $id ) === 'product' && strlen( $title ) > 30 ) {
return substr( $title, 0, 30) . '…'; // change last number to the number of characters you want
} else {
return $title;
}
}
return $title;
}
But remember that if you use page caching a mobile user may be the one to generate the cache, and thus your cache will include the change and display everywhere. Given that this specific context is WooCommerce and therefore that maybe you're not caching the product pages if you need them to be dynamic somehow, this may work anyway, but #markus-ao's comment above would be a better solution if caching is an issue.
Related
I'm working on the admin side for a Wordpress site. I have a CPT that I've added a column into and would like to sort by that column type. Based off what I've read I'm using the correct functions, but after the filter of 'manage_edit-leadership_sortable_columns' there are no changes to the admin UI. This a CPT and the hierarchy is set to 'True.' I'd read that that may affect it but couldn't find a fix. The column also appears with a red circle and '0' next to it, that I don't know where it came from. The eventual goal is to set the display page on the site to the menu order and give the client the option to sort either by Title or Last Name. Thanks!
add_filter( 'manage_leadership_posts_columns', 'smashing_filter_posts_columns' );
function smashing_filter_posts_columns( $columns ) {
$name = array();
$name['last_name'] = __( 'Last Name', 'smashing', true );
array_splice($columns, 2, 0, $name);
return $columns;
}
function smashing_leadership_column( $column, $post_id ) {
// Name column
if ( 'last_name' == $column ) {
$lastName = get_field('last_name', $post_id );
echo $lastName;
}
}
add_action( 'manage_leadership_posts_custom_column', 'smashing_leadership_column', 10, 2);
function smashing_leadership_sortable_columns( $columns ) {
$columns['last_name'] = 'last_name';
return $columns;
}
add_filter( 'manage_edit-leadership_sortable_columns', 'smashing_leadership_sortable_columns');
add_action( 'pre_get_posts', 'smashing_posts_orderby' );
function smashing_posts_orderby( $query ) {
if( ! is_admin() || ! $query->is_main_query() ) {
return;
}
if ( 'last_name' === $query->get( 'orderby') ) {
$query->set( 'orderby', 'meta_value' );
$query->set( 'meta_key', 'last_name' );
}
}
Admin Screenshot
I was able to get this to work with the following code in place of the other sortable filters.
add_filter( 'manage_edit-leadership_sortable_columns', 'my_sortable_leadership_columns' );
function my_sortable_leadership_columns( $columns ) {
$columns['firstName'] = 'first';
$columns['lastName'] = 'last';
return $columns;
}
add_action( 'pre_get_posts', 'my_leadership_orderby' );
function my_leadership_orderby( $query ) {
if( ! is_admin() )
return;
$orderby = $query->get( 'orderby');
if( 'first' == $orderby ) {
$query->set('meta_key','first_name');
$query->set('orderby','meta_value');
}
if( 'last' == $orderby ) {
$query->set('meta_key','last_name');
$query->set('orderby','meta_value');
}
}
based on this example
add_filter( 'facetwp_builder_item_value', function( $value, $item ) {
if ( 'post_excerpt' == $item['source'] ) {
$value = substr( $value, 0, 120 );
}
return $value;
}, 10, 2 );
I would like to trim post_title to a maximum limit of 120 with the (...) at the end.
This objective was not achieved with my modification and I failed several times with several combinations
// my actual attemp to trim post_title item
add_filter( 'facetwp_builder_item_value', function( $value, $item ) {
if ( 'post_title' == $item['source'] ) {
$raw_value = $item['source'];
if ( 120 < strlen( $raw_value ) ) {
$value['source'] = substr( $raw_value, 0, 120 ) . "...";
}
}
return $value;
}, 10, 2 );
following the logic of the things based on this exemple mentioned here
// Add the following to your theme's functions.php
add_filter( 'facetwp_index_row', function( $params, $class ) {
if ( 'aufsichtsbehoerden' == $params['facet_name'] ) {
$raw_value = $params['facet_value'];
if ( 50 < strlen( $raw_value ) ) {
$params['facet_value'] = substr( $raw_value, 0, 50 ); // cut off some of the value
}
}
return $params;
}, 10, 2 );
Experts in PHP and use filters are requested to join me to tell me what I missed as a modification ..
Nest your if statements, and add some way of tracking the bug.
add_filter( 'facetwp_builder_item_value', function( $value, $item ) {
if ( 'post_title' == $item['source']) {
$maxLength = 120;
echo "POST TITLE == ITEM SOURCE";
if (strlen($value) > $maxLength) {
echo "POST TITLE EXCEEDED MAX LENGTH";
$value = substr( $value, 0, $maxLength) . '…';
}
}
return $value;
}, 10, 2 );
I have added two echo functions to allow you to understand where and when the conditions are met.
Your first if is doing nothing in the first code. Notice that there are nothing between its brackets { }.
Your second code is combining both conditions correctly. If it doesn't work, 'post_title' is probably not equal to $item['source'].
I need some help with a woocommerce website with a function.php file that I edit within a plugin called WP All Import. It's linked to a plugin that imports csv & xml files.
In WP All Import I set the import title as [product_title({product_name[1]}, {size[1]}, {colour[1]})] and it picks up the items from an import.
What I'm trying to do is set the product name as a combination of variables. So in the example below, if both $size and $colour are empty I want the product name to just be $name. If $size is empty, then $name - $colour. If $ colour is empty then $name - $size. And then if they all have values $name - $colour - $size.
function product_title($name, $size, $colour)
{
$newName = $name;
if (strpos($size, "")) {
if (strpos($colour, ""))
{
$newName = "$name";
}
}
else if (strpos($size, ""))
{
$newName = "$name - $colour";
}
else if (strpos($colour, ""))
{
$newName = "$name - $size";
}
else
{
$newName = "$name - $colour - $size";
}
}
At the moment this is leaving the titles as blank. Where did I go wrong?
I've had to do this not so long ago. It's pretty tricky to understand. In short you can't use pre_get_post because the terms are not set yet. You must use save_post with wp_update_post, but there is a few thing to understand like the infinite loop case.
Don't hesitate to adapt it. I guessed that colors and sizes are custom taxonomies, just make sure the slugs matches. Modify it and have fun.
<?php
add_action( 'save_post', 'worker', 10, 3 );
function worker( $post_id, $post, $update ) {
// ... if not the admin side or if user can't edit post, then bail
if ( ! is_admin() || ! current_user_can( 'edit_post', $post_id ) )
return;
$worker = ( object ) [
'post_type' => 'product', // ... set our taxonomy in your case, "product"
'taxonomies' => [ 'colour', 'size', ], // ... set the taxonomies that we want to use in our title
];
// ... if not a post.php page or post-new.php page, then bail
$base = [
'post.php',
'post-new.php',
];
if( ! in_array( filter_input( INPUT_SERVER, 'REQUEST_URI' ), $base, true ) && $post->post_type != $worker->post_type )
return;
// ... do not update title if autosave
if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;
// ... fetch our terms and join them
$terms = join( ' - ', wp_list_pluck( wp_get_object_terms( $post_id, $worker->taxonomies ), 'name' ) );
// ... define our title structure
$self = $post->post_title . $terms . '#' . $post_id; // ... You should leave the post_id as a unique identifer, so you don't end up with 2 product with the same title
// ... https://developer.wordpress.org/reference/hooks/save_post/#avoiding-infinite-loops
remove_action( 'save_post', 'worker' );
wp_update_post( array(
'ID' => $post_id,
'post_title' => esc_attr( $self ),
'post_name' => sanitize_title( $self ),
) );
add_action( 'save_post', 'worker', 10, 3 );
}; ?>
I am trying to make it so that the url.com/my-account or the shortcode [woocommerce_my_account] displays the orders instead of the dashboard that displays "Hello User (not user)?".
The only thing I have is for after logging in which redirects to the orders instead of the dashboard, but I then going to the /my-account still displays the dashboard which I don't want.
The closest code I found that does what I want is...
function woocommerce_orders() {
$user_id = get_current_user_id();
if ($user_id == 0) {
return do_shortcode('[woocommerce_my_account]');
}else{
ob_start();
wc_get_template( 'myaccount/my-orders.php', array(
'current_user' => get_user_by( 'id', $user_id),
'order_count' => $order_count
) );
return ob_get_clean();
}
}
add_shortcode('woocommerce_orders', 'woocommerce_orders');
However, if there are no orders placed then it comes out blank(doesn't display the "No order has been made yet." with shop button) and the my account nav-sidebar doesn't show up. Would I have to make a custom page-template for this to add in the woocommerce account nav-sidebar?
Edit: If I use the orders.php instead of my-orders.php then I am able to get the "No order has been made yet." But still no sidebar-nav
You could try the following code (that is not perfect as it removes the access to the dashboard):
add_action( 'woocommerce_account_content', 'remove_dashboard_account_default', 5 );
function remove_dashboard_account_default() {
remove_action( 'woocommerce_account_content', 'woocommerce_account_content', 10 );
add_action( 'woocommerce_account_content', 'custom_account_orders', 10 );
}
function custom_account_orders( $current_page ) {
global $wp;
if ( ! empty( $wp->query_vars ) ) {
foreach ( $wp->query_vars as $key => $value ) {
// Ignore pagename param.
if ( 'pagename' === $key ) {
continue;
}
if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) {
do_action( 'woocommerce_account_' . $key . '_endpoint', $value );
return;
}
}
}
$current_page = empty( $current_page ) ? 1 : absint( $current_page );
$customer_orders = wc_get_orders( apply_filters( 'woocommerce_my_account_my_orders_query', array(
'customer' => get_current_user_id(),
'page' => $current_page,
'paginate' => true,
) ) );
wc_get_template(
'myaccount/orders.php',
array(
'current_page' => absint( $current_page ),
'customer_orders' => $customer_orders,
'has_orders' => 0 < $customer_orders->total,
)
);
}
Code goes in function.php file of your active child theme (or active theme). Tested and work.
There's a much easier way: simply catch WordPress's parse_request, check if the request is for my-account (or whatever your account page slug is) and perform a redirect:
function vnm_wc_redirect_account_dashboard( $wp ) {
if ( !is_admin() ) {
// Uncomment the following line if you want to see what the current request is
//die( $wp->request );
// The following will only match if it's the root Account page; all other endpoints will be left alone
if ( $wp->request === 'my-account' ) {
wp_redirect( site_url( '/my-account/orders/' ) );
exit;
}
}
}
add_action( 'parse_request', 'vnm_wc_redirect_account_dashboard', 10, 1 );
I used code of LoicTheAztec and another complementary snippet, which removes dashboard tab:
// Remove or rename my account page navigation links (removes downloads and dashboard).
add_filter ( 'woocommerce_account_menu_items', 'my_account_menu_order' );
function my_account_menu_order() {
$menuOrder = array(
'orders' => __( 'Orders', 'woocommerce' ),
// 'downloads' => __( 'Download', 'woocommerce' ),
'edit-address' => __( 'Addresses', 'woocommerce' ),
'edit-account' => __( 'Account details', 'woocommerce' ),
'customer-logout' => __( 'Logout', 'woocommerce' ),
// 'dashboard' => __( 'Dashboard', 'woocommerce' )
);
return $menuOrder;
}
I also created a snippet which causes orders tab to be highlighted by default. It's done through adding active class and then CSS opacity: 1 highlights it. Script will show only in account section to avoid bloat where it isn't needed:
// Make orders link highlighted by default in my account section.
add_action('wp_footer', 'taisho_dashboard_orders_highlight');
function taisho_dashboard_orders_highlight() {
if (!is_account_page()) return; // Account section only
global $wp;
$acc_url = get_permalink( get_option( 'woocommerce_myaccount_page_id' ));
$my_acc = rtrim( $acc_url , '/' );
$my_acc = explode( '/', $my_acc );
?>
<script type="text/javascript">
var dashboard_active = <?php echo $wp->request === end($my_acc) ?>;
jQuery(document).ready(function($) {
$('.woocommerce-MyAccount-navigation-link--orders').toggleClass('is-active', dashboard_active);
});
</script>
<?php
}
Using parse_request action:
add_action( 'parse_request', function ( $wp ) {
// Prevent the redirection, in the case,
// the user is not logged in (no login, no orders)
if (!is_user_logged_in()) return false;
if ( $wp->request === 'my-account' ) {
wp_redirect( home_url( '/my-account/orders/' ) );
exit;
}
}, 10, 1 );
As of today there's only one workaround to set another My Account tab as default and also preserve the "Dashboard" tab.
Let's say we want to default to the "Downloads" tab. We need to:
replace the “Dashboard” tab content with the “Downloads” tab content
rename the “Dashboard” tab to “Downloads”
hide the original “Downloads” tab as we already have it now
readd the “Dashboard” tab
This is a little complex to paste here so I'll cover point 1 only which is the most difficult one. The rest can be easily found on StackOverflow or this Business Bloomer tutorial as it's basic rename/remove/add My Account tabs work.
As per point 1, we need to overwrite the woocommerce_account_content() function which is responsible to display the "Dashboard" content by default in case we're on the My Account and there are no query vars or there is a non-empty query var called "pagename".
I will therefore remove the WooCommerce function, and add my version instead:
add_action( 'woocommerce_account_content', 'bbloomer_myaccount_replace_dashboard_content', 1 );
function bbloomer_myaccount_replace_dashboard_content() {
remove_action( 'woocommerce_account_content', 'woocommerce_account_content', 10 );
add_action( 'woocommerce_account_content', 'bbloomer_account_content' );
}
Now I go define my custom bbloomer_account_content() function, and I basically tell the system that:
if there is no query var or if there is and a query var called "pagename" is not empty, I echo my custom content (the "Downloads" tab content in my case"
otherwise, it means I am on an endpoint URL, and therefore I copy whatever was inside woocommerce_account_content() to return the tab content
Code:
function bbloomer_account_content() {
global $wp;
if ( empty( $query_vars = $wp->query_vars ) || ( ! empty( $query_vars ) && ! empty( $query_vars['pagename'] ) ) ) {
woocommerce_account_downloads();
} else {
foreach ( $wp->query_vars as $key => $value ) {
if ( 'pagename' === $key ) {
continue;
}
if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) {
do_action( 'woocommerce_account_' . $key . '_endpoint', $value );
return;
}
}
}
}
In this way, if I go to "example.com/my-account" I will see the "Downloads" tab content; if I switch tab and go to "Dashboard" or "Orders", I will see their original content instead.
Remember that this is only part 1 and therefore additional workaround is needed with parts 2, 3 and 4 to really make it work.
I'm trying to implement a text field validation for field name VoucherNumber that requires the code to be in a certain pattern which is 'WWV-'followed by 4 numbers.
I was successfully able to implement this on google docs using the following expression ^[W]WV-[0-9][0-9][0-9][0-9].
I researched through various answers and attempted to add this code in functions.php but it didn't work. It would just show that the form is being sent (spinning wheel) but it would not be sent even after 5 minutes.
add_filter( 'wpcf7_validate_text*', 'validate_voucher_number', 20, 2 );
function validate_voucher_number( $result, $tag ) {
$tag = new WPCF7_FormTag( $tag );
if ( 'VoucherNumber' == $tag->name ) {
$VoucherNumber = isset( $_POST['VoucherNumber'] ) ? trim( $_POST['VoucherNumber'] ) : '';
if ( ! preg_match ( "^[W]WV-[0-9][0-9][0-9][0-9]" , $VoucherNumber) ){
$result->invalidate( $tag, "Voucher number is invalid" );
}
}
return $result;
}
Just make sure that your field name is VoucherNumber and try this code :
add_filter( 'wpcf7_validate_text', 'vouchervalidation', 20, 2 );
function vouchervalidation( $result, $tag ) {
if ( 'VoucherNumber' == $tag->name ) {
$VoucherNumber = isset( $_POST['VoucherNumber'] ) ? trim( $_POST['VoucherNumber'] ) : '';
if ( ! preg_match ( "^[W]WV-[0-9][0-9][0-9][0-9]" , $VoucherNumber) ){
$result->invalidate( $tag, "Voucher number is invalid" );
}
}
return $result;
}