'yes', 'product_note' => 'no', /* translators: {date}: the first free date */ 'earliest_text' => __( 'Delivery possible from {date}', 'dayslot' ), /* translators: {date}: the chosen date and time */ 'cart_date_text' => __( 'Your delivery: {date}', 'dayslot' ), ) ); } ); /** * The first date a customer can choose (Y-m-d), or '' when none in the booking window. */ function dayslot_earliest_date( $ctx = array() ) { $ctx = dayslot_ctx( $ctx ); $off = array_flip( dayslot_unavailable_dates( $ctx ) ); $today = dayslot_today(); $max = (int) dayslot_get_settings()['max_days']; for ( $i = dayslot_min_days( $ctx ); $i <= $max; $i++ ) { $key = $today->modify( '+' . $i . ' days' )->format( 'Y-m-d' ); if ( ! isset( $off[ $key ] ) ) { return $key; } } return ''; } /** * The way customers get their order when there is only one (pickup only shops say "pickup"). */ function dayslot_main_mode() { $modes = dayslot_modes(); return isset( $modes['delivery'] ) ? 'delivery' : (string) key( $modes ); } /** * The line of text: the chosen date (and time) when there is one, else the first free date. */ function dayslot_cart_note_text( $products = null, $in_cart = false ) { $s = dayslot_get_settings(); $choice = $in_cart ? dayslot_session_get() : dayslot_empty_choice(); if ( $choice['date'] && dayslot_parse_date( $choice['date'] ) && $choice['date'] >= dayslot_today()->format( 'Y-m-d' ) ) { $when = dayslot_format_date( $choice['date'] ) . ( $choice['slot'] ? ', ' . dayslot_format_slot( $choice['slot'] ) : '' ); return str_replace( '{date}', $when, $s['cart_date_text'] ); } $date = dayslot_earliest_date( array( 'mode' => dayslot_main_mode(), 'items' => $products, ) ); if ( ! $date ) { return ''; } $today = dayslot_today(); $is_today = $today->format( 'Y-m-d' ) === $date; $is_next = $today->modify( '+1 day' )->format( 'Y-m-d' ) === $date; // The shop's own text keeps {date}; the default one says today / tomorrow in a whole sentence ("od jutra", not "od jutro"). if ( dayslot_defaults()['earliest_text'] === $s['earliest_text'] && ( $is_today || $is_next ) ) { return $is_today ? __( 'Delivery possible today', 'dayslot' ) : __( 'Delivery possible from tomorrow', 'dayslot' ); } if ( $is_today ) { $when = __( 'today', 'dayslot' ); } elseif ( $is_next ) { $when = __( 'tomorrow', 'dayslot' ); } else { $when = dayslot_format_date( $date ); } return str_replace( '{date}', $when, $s['earliest_text'] ); } /** * The line as HTML. With $refresh (a product id, or 0 for no product) the line can be served from a page cache: * a few lines of script ask for a fresh text when the page is older than 15 minutes ("tomorrow" yesterday is "today"). */ function dayslot_cart_note_html( $products = null, $in_cart = false, $refresh = null ) { $text = dayslot_cart_note_text( $products, $in_cart ); if ( '' === $text ) { return ''; } $attr = ''; if ( null !== $refresh ) { $attr = sprintf( ' data-dys-note="%d" data-dys-made="%d"', (int) $refresh, time() ); dayslot_note_refresh_script(); } return '

' . esc_html( $text ) . '

'; } /** * The refresh script for cached pages (inline, no file; added once, in the footer). */ function dayslot_note_refresh_script() { static $done = false; if ( $done ) { return; } $done = true; wp_register_script( 'dys-note', false, array(), DAYSLOT_VERSION, true ); wp_enqueue_script( 'dys-note' ); wp_add_inline_script( 'dys-note', '(function(){var u=' . wp_json_encode( WC_AJAX::get_endpoint( 'dayslot_note' ) ) . ';document.querySelectorAll("[data-dys-note]").forEach(function(p){if(Date.now()/1000-p.getAttribute("data-dys-made")<900||!window.fetch){return;}fetch(u+"&product="+p.getAttribute("data-dys-note"),{credentials:"same-origin"}).then(function(r){return r.json();}).then(function(j){if(j&&j.success&&j.data){p.textContent=j.data;}}).catch(function(){});});})();' ); } // Fresh text for a cached page. add_action( 'wc_ajax_dayslot_note', function () { $product = isset( $_GET['product'] ) ? wc_get_product( absint( $_GET['product'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only public text. nocache_headers(); wp_send_json_success( dayslot_cart_note_text( $product ? array( $product ) : array() ) ); } ); // Classic cart: above the totals. add_action( 'woocommerce_before_cart_totals', function () { if ( dayslot_cart_note_on() ) { echo wp_kses_post( dayslot_cart_note_html( dayslot_cart_products(), true ) ); } } ); /** * Should the cart line show now (setting on, a cart that needs shipping)? */ function dayslot_cart_note_on() { return 'yes' === dayslot_get_settings()['cart_note'] && function_exists( 'WC' ) && WC()->cart && WC()->cart->needs_shipping(); } // Side carts: FunnelKit Cart (under the totals) and the WooCommerce mini cart (Elementor menu cart, widgets). foreach ( array( 'fkcart_after_order_summary', 'woocommerce_widget_shopping_cart_before_buttons' ) as $dayslot_hook ) { add_action( $dayslot_hook, function () { if ( dayslot_cart_note_on() ) { echo wp_kses_post( dayslot_cart_note_html( dayslot_cart_products(), true ) ); } } ); } unset( $dayslot_hook ); // Cart block: above the cart. add_filter( 'render_block_woocommerce/cart', function ( $html ) { if ( ! dayslot_cart_note_on() ) { return $html; } return dayslot_cart_note_html( dayslot_cart_products(), true ) . $html; } ); /** * The product page line for the page's own product, once per page ('' for other products, e.g. related ones). */ function dayslot_product_note_html( $product_id ) { static $done = false; if ( $done || 'yes' !== dayslot_get_settings()['product_note'] || ! function_exists( 'is_product' ) || ! is_product() || (int) get_queried_object_id() !== (int) $product_id ) { return ''; } $product = wc_get_product( $product_id ); if ( ! $product || $product->is_virtual() ) { return ''; } $done = true; return dayslot_cart_note_html( array( $product ), false, $product->get_id() ); } // Product pages (off by default): under the price — classic themes, Elementor Pro product templates, block themes. add_action( 'woocommerce_single_product_summary', function () { global $product; if ( $product instanceof WC_Product ) { echo wp_kses_post( dayslot_product_note_html( $product->get_id() ) ); } }, 25 ); add_filter( 'elementor/widget/render_content', function ( $content, $widget ) { global $product; if ( 'woocommerce-product-price' === $widget->get_name() && $product instanceof WC_Product ) { $content .= dayslot_product_note_html( $product->get_id() ); } return $content; }, 10, 2 ); add_filter( 'render_block_woocommerce/product-price', function ( $html, $block, $instance = null ) { $id = $instance && isset( $instance->context['postId'] ) ? $instance->context['postId'] : get_the_ID(); return $html . dayslot_product_note_html( $id ); }, 10, 3 ); // [dayslot_earliest] anywhere: "Delivery possible from …". add_shortcode( 'dayslot_earliest', function () { $product = function_exists( 'wc_get_product' ) ? wc_get_product( get_the_ID() ) : null; return dayslot_cart_note_html( $product ? array( $product ) : array(), false, $product ? $product->get_id() : 0 ); } );