I have a store that has long product descriptions and I want to truncate the description to show 100 words and then a button that says "read more" and "read less".
Actually I am using Reduce product long description in Woocommerce answer code that can limit short description length by specific word number count and it works.
This requires a minor tweak to the existing code that instead of leaving out some of the $content, keeps all of the $content but shows only part of it until the read button is clicked.
Then it uses jQuery which has control over the read more/less button where either all $content or only part of it is shown.
NOTE: CSS (styling) and further jQuery adjustments are theme dependent. This answer contains the basis of a read more / less button. Each theme has different and specific requirements so further adaptation of the display/functionality must be provided by yourself
Explanation via comment tags provided in the code
// Shorten product long descrition with read more button
function filter_the_content( $content ) {
// Only for single product pages
if( ! is_product() ) return $content;
// Set the limit of words
$limit = 14;
// Strip p tags if needed
$content = str_replace( array( '<p>', '</p>' ), '', $content );
// If the content is longer than the predetermined limit
if ( str_word_count( $content, 0 ) > $limit ) {
// Returns an associative array, where the key is the numeric position of the word inside the string and the value is the actual word itself
$arr = str_word_count( $content, 2 );
// Return all the keys or a subset of the keys of an array
$pos = array_keys( $arr );
// First part
$text = '<p>' . substr( $content, 0, $pos[$limit] ) . '<span id="dots">...</span>';
// More part
$text .= '<span id="more">' . substr( $content, $pos[$limit] ) . '</span></p>';
// Read button
$text .= '<button id="read-button"></button>';
$content = force_balance_tags( $text ); // needded
}
?>
<script type="text/javascript">
jQuery(document).ready( function ($) {
// Settings
var read_more_btn_txt = 'Read more';
var read_less_btn_txt = 'Read less';
// Selectors
var more = '#more';
var read_button = '#read-button';
var dots = '#dots';
// On load
$( more ).hide();
$( read_button ).html( read_more_btn_txt );
// On click
$( read_button ).on( 'click', function() {
if ( $( more ).is( ':hidden' ) ) {
$( more ).show();
$( dots ).hide();
$( read_button ).html( read_less_btn_txt );
} else {
$( more ).hide();
$( dots ).show();
$( read_button ).html( read_more_btn_txt );
}
});
});
</script>
<?php
return $content;
}
add_filter( 'the_content', 'filter_the_content', 10, 1 );
Related
I'm trying to validate a custom checkout field against a regex expression so that it is a very specifically formatted date.
Here's my code:
add_action('woocommerce_after_checkout_validation', array($this, 'bkf_dd_checkout_validation'), 10, 2 );
add_action('wp_footer', array($this, 'bkf_dd_checkout_validation_js'));
function bkf_dd_checkout_validation( $fields, $errors ){
if( ! preg_match( '/[a-zA-Z]{6,9}\,\ \d{1,2}\ [a-zA-Z]{3,9}\ \d{4}/', $fields['delivery_date'] ) ){
$errors->add( 'validation', 'Please select a <strong>Delivery Date</strong> via the datepicker.' );
}
}
function bkf_dd_checkout_validation_js(){
if( ! is_checkout() ) {
return;
}
?>
<script>
jQuery(function($){
$( 'body' ).on( 'blur change', '#delivery_date', function(){
const wrapper = $(this).closest( '.form-row' );
if( ! /[a-zA-Z]{6,9}\,\ \d{1,2}\ [a-zA-Z]{3,9}\ \d{4}/.test( $(this).val() ) ) {
wrapper.addClass( 'woocommerce-invalid' );
wrapper.removeClass( 'woocommerce-validated' );
} else {
wrapper.addClass( 'woocommerce-validated' );
}
});
});
</script>
<?php
}
The JS is working, but the error is being set regardless of the value, ie. this displays even if field is valid (and set as green by the JS)
(the date format: Friday, 30 December 2022)
Any ideas?
Solution as provided by Vijay Hardaha
Replace $fields['delivery_date'] with $_POST['delivery_date']
I have payment needed to wait for JS callback token, so on the checkout page, the flow will have the customer check the token button, then click and get the token will apply to the custom field,
in the end, if the custom field is not empty show the order button.
custom field
function techiepress_payleo_description_fields( $description, $payment_id ) {
if( 'payleos' !== $payment_id) {
return $description;
};
// $json = json_encode(WC()->cart);
ob_start();
echo '<div id="Payment">'; // it will create get token button
echo '</div>';
woocommerce_form_field( 'payment_number', array(
'type' => 'text',
'label' => __("credit card", "payleo_payment_plus"),
'required' => true,
),'');
$description .= ob_get_clean();
return $description;
}
hidden order button
add_filter('woocommerce_order_button_html', 'remove_place_order_button_for_specific_payments' );
function remove_place_order_button_for_specific_payments( $button ) {
// HERE define your targeted payment(s) method(s) in the array
$targeted_payments_methods = array('payleos');
$chosen_payment_method = WC()->session->get('chosen_payment_method'); // The chosen payment
// For matched payment(s) method(s), we remove place order button (on checkout page)
if( in_array( $chosen_payment_method, $targeted_payments_methods ) && ! is_wc_endpoint_url() ) {
$button = '';
}
return $button;
}
// jQuery - Update checkout on payment method change
add_action( 'wp_footer', 'custom_checkout_jquery_script' );
function custom_checkout_jquery_script() {
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?>
<script type="text/javascript">
jQuery( function($){
$('form.checkout').on('change', 'input[name="payment_method"]', function(){
$(document.body).trigger('update_checkout');
});
});
</script>
<?php
endif;
}
I would like to know how can show the place order button when the custom field has value?
I have a custom filed for specific shipping methods (see screenshot).
Is there a hook/action to validate this field before proceeding the checkout. When I set the field to required the validation will fire at the checkout page but I want it at the cart page.
Validation:
//Validate the custom selection field
add_action('woocommerce_checkout_process', 'carrier_company_checkout_validation');
function carrier_company_checkout_validation() {
Load settings and convert them in variables
extract( custom_field_settings() );
if( isset( $_POST[$field_id] ) && empty( $_POST[$field_id] ) )
wc_add_notice(
sprintf( __("Please select a %s as it is a required field.","woocommerce"),
'<strong>' . $label_name . '</strong>'
), "error" );
}
** UPDATE: **
like #LoicTheAztec mentioned here the complete code.
Everything is working fine. The custom input value from cart will be shown on checkout and saved in the DB after order confirmation. But I don't know where I have to put the validation on the cart page when the custom input is empty because everything on cart is on ajax.
// ##########################################
// Add custom fields to a specific selected shipping method
// ##########################################
// Custom function that handle your settings
function delivery_date_settings(){
return array(
'field_id' => 'delivery_date', // Field Id
'field_type' => 'text', // Field type
'field_label' => 'label text', // Leave empty value if the first option has a text (see below).
'label_name' => __("Lieferdatum","woocommerce"), // for validation and as meta key for orders
);
}
// Display the custom checkout field
add_action( 'woocommerce_after_shipping_rate', 'carrier_company_custom_select_field', 20, 2 );
function carrier_company_custom_select_field( $method, $index ) {
if( $method->id == 'flat_rate:2' || $method->id == 'free_shipping:1') {
extract( delivery_date_settings() ); // Load settings and convert them in variables
$chosen = WC()->session->get('chosen_shipping_methods'); // The chosen methods
$value = WC()->session->get($field_id);
$value = WC()->session->__isset($field_id) ? $value : WC()->checkout->get_value('_'.$field_id);
$options = array(); // Initializing
$chosen_method_id = WC()->session->chosen_shipping_methods[ $index ];
if($chosen_method_id == 'flat_rate:2' || $method->id == 'free_shipping:1' ){
echo '<div class="custom-date-field">';
woocommerce_form_field( $field_id, array(
'type' => $field_type,
'label' => $field_label, // Not required if the first option has a text.
'class' => array('form-row-wide datepicker ' . $field_id . '-' . $field_type ),
'required' => false,
), $value );
echo '</div>';
// Jquery: Enable the Datepicker
?>
<script language="javascript">
jQuery( function($){
$('.datepicker input').datepicker({
dateFormat: 'dd.mm.yy', // ISO formatting date
});
});
</script>
<?php
}
}
}
// jQuery code (client side) - Ajax sender
add_action( 'wp_footer', 'carrier_company_script_js' );
function carrier_company_script_js() {
// Only cart & checkout pages
if( is_cart() || ( is_checkout() && ! is_wc_endpoint_url() ) ):
// Load settings and convert them in variables
extract( lieferdatum_settings() );
$js_variable = is_cart() ? 'wc_cart_params' : 'wc_checkout_params';
// jQuery Ajax code
?>
<script type="text/javascript">
jQuery( function($){
if (typeof <?php echo $js_variable; ?> === 'undefined')
return false;
$(document.body).on( 'change', 'input#<?php echo $field_id; ?>', function(){
var value = $(this).val();
$.ajax({
type: 'POST',
url: <?php echo $js_variable; ?>.ajax_url,
data: {
'action': 'delivery_date',
'value': value
},
success: function (result) {
console.log(result); // Only for testing (to be removed)
}
});
});
});
</script>
<?php
endif;
}
// The Wordpress Ajax PHP receiver
add_action( 'wp_ajax_delivery_date', 'set_carrier_company_name' );
add_action( 'wp_ajax_nopriv_delivery_date', 'set_carrier_company_name' );
function set_carrier_company_name() {
if ( isset($_POST['value']) ){
// Load settings and convert them in variables
extract( delivery_date_settings() );
if( empty($_POST['value']) ) {
$value = 0;
$label = 'Empty';
} else {
$value = $label = esc_attr( $_POST['value'] );
}
// Update session variable
WC()->session->set( $field_id, $value );
// Send back the data to javascript (json encoded)
echo $label;
die();
}
}
// Save custom field as order meta data
add_action( 'woocommerce_checkout_create_order', 'save_carrier_company_as_order_meta', 30, 1 );
function save_carrier_company_as_order_meta( $order ) {
// Load settings and convert them in variables
extract( delivery_date_settings() );
if( isset( $_POST[$field_id] ) && ! empty( $_POST[$field_id] ) ) {
$order->update_meta_data( '_'.$field_id, esc_attr($_POST[$field_id]) );
WC()->session->__unset( $field_id ); // remove session variable
}
}
// Display custom field in admin order pages
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'admin_order_display_carrier_company', 30, 1 );
function admin_order_display_carrier_company( $order ) {
// Load settings and convert them in variables
extract( delivery_date_settings() );
$carrier = $order->get_meta( '_'.$field_id ); // Get carrier company
if( ! empty($carrier) ) {
// Display
echo '<p><strong>' . $label_name . '</strong>: ' . $carrier . '</p>';
}
}
// Display carrier company after shipping line everywhere (orders and emails)
add_filter( 'woocommerce_get_order_item_totals', 'display_carrier_company_on_order_item_totals', 1000, 3 );
function display_carrier_company_on_order_item_totals( $total_rows, $order, $tax_display ){
// Load settings and convert them in variables
extract( delivery_date_settings() );
$carrier = $order->get_meta( '_'.$field_id ); // Get carrier company
if( ! empty($carrier) ) {
$new_total_rows = [];
// Loop through order total rows
foreach( $total_rows as $key => $values ) {
$new_total_rows[$key] = $values;
// Inserting the carrier company under shipping method
if( $key === 'shipping' ) {
$new_total_rows[$field_id] = array(
'label' => $label_name,
'value' => $carrier,
);
}
}
return $new_total_rows;
}
return $total_rows;
}
I think the hook you're looking for is woocommerce_check_cart_items, that's the one that validates the cart items before proceeding to checkout.
I'm not 100% sure whether you'll be able to get the field in the same way using $_POST but it's worth a go.
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;
}
I am using Wordpress 3.5, I have a custom post (sp_product) with a metabox and some input field. One of those input (sp_title).
I want to Search by the custom post title name by typing in my input (sp_title) field and when i press add button (that also in my custom meta box), It will find that post by that Title name and bring some post meta data into this Meta box and show into other field.
Here in this picture (Example)
Search
Click Button
Get some value by AJAX from a custom post.
Please give me a example code (just simple)
I will search a simple custom post Title,
Click a button
Get the Title of that post (that i search or match) with any other post meta value, By AJAX (jQuery-AJAX).
Please Help me.
I was able to find the lead because one of my plugins uses something similar to Re-attach images.
So, the relevant Javascript function is findPosts.open('action','find_posts').
It doesn't seem well documented, and I could only found two articles about it:
Find Posts Dialog Box
Using Built-in Post Finder in Plugins
Tried to implement both code samples, the modal window opens but dumps a -1 error. And that's because the Ajax call is not passing the check_ajax_referer in the function wp_ajax_find_posts.
So, the following works and it's based on the second article. But it has a security breach that has to be tackled, which is wp_nonce_field --> check_ajax_referer. It is indicated in the code comments.
To open the Post Selector, double click the text field.
The jQuery Select needs to be worked out.
Plugin file
add_action( 'load-post.php', 'enqueue_scripts_so_14416409' );
add_action( 'add_meta_boxes', 'add_custom_box_so_14416409' );
add_action( 'wp_ajax_find_posts', 'replace_default_ajax_so_14416409', 1 );
/* Scripts */
function enqueue_scripts_so_14416409() {
# Enqueue scripts
wp_enqueue_script( 'open-posts-scripts', plugins_url('open-posts.js', __FILE__), array('media', 'wp-ajax-response'), '0.1', true );
# Add the finder dialog box
add_action( 'admin_footer', 'find_posts_div', 99 );
}
/* Meta box create */
function add_custom_box_so_14416409()
{
add_meta_box(
'sectionid_so_14416409',
__( 'Select a Post' ),
'inner_custom_box_so_14416409',
'post'
);
}
/* Meta box content */
function inner_custom_box_so_14416409( $post )
{
?>
<form id="emc2pdc_form" method="post" action="">
<?php wp_nonce_field( 'find-posts', '_ajax_nonce', false); ?>
<input type="text" name="kc-find-post" id="kc-find-post" class="kc-find-post">
</form>
<?php
}
/* Ajax replacement - Verbatim copy from wp_ajax_find_posts() */
function replace_default_ajax_so_14416409()
{
global $wpdb;
// SECURITY BREACH
// check_ajax_referer( '_ajax_nonce' );
$post_types = get_post_types( array( 'public' => true ), 'objects' );
unset( $post_types['attachment'] );
$s = stripslashes( $_POST['ps'] );
$searchand = $search = '';
$args = array(
'post_type' => array_keys( $post_types ),
'post_status' => 'any',
'posts_per_page' => 50,
);
if ( '' !== $s )
$args['s'] = $s;
$posts = get_posts( $args );
if ( ! $posts )
wp_die( __('No items found.') );
$html = '<table class="widefat" cellspacing="0"><thead><tr><th class="found-radio"><br /></th><th>'.__('Title').'</th><th class="no-break">'.__('Type').'</th><th class="no-break">'.__('Date').'</th><th class="no-break">'.__('Status').'</th></tr></thead><tbody>';
foreach ( $posts as $post ) {
$title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
switch ( $post->post_status ) {
case 'publish' :
case 'private' :
$stat = __('Published');
break;
case 'future' :
$stat = __('Scheduled');
break;
case 'pending' :
$stat = __('Pending Review');
break;
case 'draft' :
$stat = __('Draft');
break;
}
if ( '0000-00-00 00:00:00' == $post->post_date ) {
$time = '';
} else {
/* translators: date format in table columns, see http://php.net/date */
$time = mysql2date(__('Y/m/d'), $post->post_date);
}
$html .= '<tr class="found-posts"><td class="found-radio"><input type="radio" id="found-'.$post->ID.'" name="found_post_id" value="' . esc_attr($post->ID) . '"></td>';
$html .= '<td><label for="found-'.$post->ID.'">' . esc_html( $title ) . '</label></td><td class="no-break">' . esc_html( $post_types[$post->post_type]->labels->singular_name ) . '</td><td class="no-break">'.esc_html( $time ) . '</td><td class="no-break">' . esc_html( $stat ). ' </td></tr>' . "\n\n";
}
$html .= '</tbody></table>';
$x = new WP_Ajax_Response();
$x->add( array(
'data' => $html
));
$x->send();
}
Javascript file open-posts.js
jQuery(document).ready(function($) {
// Find posts
var $findBox = $('#find-posts'),
$found = $('#find-posts-response'),
$findBoxSubmit = $('#find-posts-submit');
// Open
$('input.kc-find-post').live('dblclick', function() {
$findBox.data('kcTarget', $(this));
findPosts.open();
});
// Insert
$findBoxSubmit.click(function(e) {
e.preventDefault();
// Be nice!
if ( !$findBox.data('kcTarget') )
return;
var $selected = $found.find('input:checked');
if ( !$selected.length )
return false;
var $target = $findBox.data('kcTarget'),
current = $target.val(),
current = current === '' ? [] : current.split(','),
newID = $selected.val();
if ( $.inArray(newID, current) < 0 ) {
current.push(newID);
$target.val( current.join(',') );
}
});
// Double click on the radios
$('input[name="found_post_id"]', $findBox).live('dblclick', function() {
$findBoxSubmit.trigger('click');
});
// Close
$( '#find-posts-close' ).click(function() {
$findBox.removeData('kcTarget');
});
});