Last active
January 8, 2025 16:30
-
-
Save damiencarbery/269059efe6d7be230ab3682ffa087031 to your computer and use it in GitHub Desktop.
WooCommerce - Schedule showing and hiding of products - Add the ability to show and hide a product on specified dates. https://www.damiencarbery.com/2025/01/schedule-showing-and-hiding-of-woocommerce-products/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Plugin Name: WooCommerce - Schedule showing and hiding of products | |
Plugin URI: https://www.damiencarbery.com/2025/01/schedule-showing-and-hiding-of-woocommerce-products/ | |
Description: Add the ability to show and hide a product on specified dates. | |
Author: Damien Carbery | |
Author URI: https://www.damiencarbery.com | |
Version: 0.1.20250108 | |
Requires Plugins: woocommerce | |
*/ | |
class ScheduleHideProducts { | |
private $show_hide_dates_start_key; | |
private $show_hide_dates_end_key; | |
// Returns an instance of this class. | |
public static function get_instance() { | |
if ( null == self::$instance ) { | |
self::$instance = new self; | |
} | |
return self::$instance; | |
} | |
// Initialize the plugin variables. | |
public function __construct() { | |
// The custom meta key for the start and end dates. | |
$this->show_hide_dates_start_key = '_show_hide_dates_start'; | |
$this->show_hide_dates_end_key = '_show_hide_dates_end'; | |
$this->init(); | |
} | |
// Set up WordPress specfic actions. | |
public function init() { | |
// Declare that this plugin supports WooCommerce HPOS. | |
// This plugin does not interact with WooCommerce orders so it doesn't have to do anything special to support HPOS. | |
add_action( 'before_woocommerce_init', function() { | |
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { | |
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); | |
} | |
} ); | |
// Add the date fields to the Product/General tab. | |
add_action( 'woocommerce_product_options_pricing', array( $this, 'add_hide_date' ) ); | |
// Save the date fields values if set. | |
add_action( 'woocommerce_process_product_meta', array( $this, 'save_show_hide_dates' ), 10, 2 ); | |
// Scheduled check for products to show or hide. | |
add_action( 'woocommerce_scheduled_sales', array( $this, 'scheduled_show_hide_check' ) ); | |
// For debugging. | |
add_shortcode( 'show_hide', array( $this, 'show_hide_shortcode' ) ); | |
} | |
public function show_hide_shortcode() { | |
$product_id = 32; | |
$visibility_terms = $this->get_visibility_terms( $product_id ); | |
echo '<pre>', var_export( $visibility_terms, true ), '</pre>'; | |
array_push( $visibility_terms, 'exclude-from-catalog', 'exclude-from-search' ); | |
echo '<pre>', var_export( $visibility_terms, true ), '</pre>'; | |
} | |
// Get the terms without the 'exclude-from-catalog' or 'exclude-from-search' terms. | |
private function get_visibility_terms( $product_id ) { | |
// Get the term slugs. | |
$visibility_terms = wp_list_pluck( wp_get_object_terms( $product_id, 'product_visibility' ), 'slug' ); | |
// Remove 'exclude-from-catalog' or 'exclude-from-search' terms, if present. | |
$visibility_terms = array_filter( $visibility_terms, function( $x ) { return $x != 'exclude-from-catalog' && $x != 'exclude-from-search'; } ); | |
return $visibility_terms; | |
} | |
// Add the date fields to the Product/General tab. | |
public function add_hide_date() { | |
$post_id = get_the_ID(); | |
$product_type = WC_Product_Factory::get_product_type( $post_id ); | |
$classname = WC_Product_Factory::get_product_classname( $post_id, $product_type ? $product_type : 'simple' ); | |
$product = new $classname( $post_id ); | |
$show_hide_dates_start_timestamp = $product->get_meta( $this->show_hide_dates_start_key, true, 'edit' ); | |
$show_hide_dates_end_timestamp = $product->get_meta( $this->show_hide_dates_end_key, true, 'edit' ); | |
// Convert the timestamp into Y-m-d format. | |
$show_hide_dates_start = $show_hide_dates_start_timestamp ? date_i18n( 'Y-m-d', $show_hide_dates_start_timestamp ) : ''; | |
$show_hide_dates_end = $show_hide_dates_end_timestamp ? date_i18n( 'Y-m-d', $show_hide_dates_end_timestamp ) : ''; | |
// Attach DatePicker to the two fields. | |
echo " | |
<script> | |
jQuery( function ( $ ) { | |
$( '.show_hide_dates_fields' ).each( function () { | |
$( this ) | |
.find( 'input' ) | |
.datepicker( { | |
defaultDate: '', | |
dateFormat: 'yy-mm-dd', | |
numberOfMonths: 1, | |
showButtonPanel: true, | |
} ); | |
} ); | |
$( '#woocommerce-product-data' ).on( | |
'click', | |
'.cancel_show_hide_schedule', | |
function () { | |
var wrap = $( this ).closest( 'div, table' ); | |
//$( this ).hide(); // Hide the 'Cancel' link. | |
wrap.find( '.show_hide_dates_fields' ).find( 'input' ).val( '' ); | |
return false; | |
} | |
); | |
} ); | |
</script> | |
"; | |
echo ' | |
<style> | |
.woocommerce_options_panel .show_hide_dates_fields .short:first-of-type { margin-bottom: 1em; } | |
.woocommerce_options_panel .show_hide_dates_fields .short:nth-of-type(2) { clear: left; } | |
</style> | |
<p class="form-field show_hide_dates_fields"> | |
<label for="'.$this->show_hide_dates_start_key.'">' . esc_html__( 'Show/hide dates', 'woocommerce' ) . '</label> | |
<input type="text" class="short" name="'.$this->show_hide_dates_start_key.'" id="'.$this->show_hide_dates_start_key.'" value="' . esc_attr( $show_hide_dates_start ) . '" placeholder="' . esc_html( _x( 'Starting…', 'placeholder', 'woocommerce' ) ) . ' YYYY-MM-DD" maxlength="10" pattern="' . esc_attr( apply_filters( 'woocommerce_date_input_html_pattern', '[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])' ) ) . '" /> | |
<input type="text" class="short" name="'.$this->show_hide_dates_end_key.'" id="'.$this->show_hide_dates_end_key.'" value="' . esc_attr( $show_hide_dates_end ) . '" placeholder="' . esc_html( _x( 'Ending…', 'placeholder', 'woocommerce' ) ) . ' YYYY-MM-DD" maxlength="10" pattern="' . esc_attr( apply_filters( 'woocommerce_date_input_html_pattern', '[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])' ) ) . '" />' . | |
'<a href="#" class="description cancel_show_hide_schedule">' . esc_html__( 'Cancel', 'woocommerce' ) . '</a>' . wc_help_tip( __( 'The product will be shown at 00:00:00 of "Starting" date and hidden at 23:59:59 of "Ending" date.', 'woocommerce' ) ) . ' | |
</p>'; | |
} | |
// Save the date fields values if set. | |
public function save_show_hide_dates( $post_id, $post ) { | |
$product_type = empty( $_POST['product-type'] ) ? WC_Product_Factory::get_product_type( $post_id ) : sanitize_title( wp_unslash( $_POST['product-type'] ) ); | |
$classname = WC_Product_Factory::get_product_classname( $post_id, $product_type ? $product_type : 'simple' ); | |
$product = new $classname( $post_id ); | |
// Handle show/hide dates. | |
$show_hide_dates_start = ''; | |
$show_hide_dates_end = ''; | |
// Force 'date from' to beginning of day. | |
if ( isset( $_POST['_show_hide_dates_start'] ) ) { | |
$show_hide_dates_start = wc_clean( wp_unslash( $_POST['_show_hide_dates_start'] ) ); | |
if ( ! empty( $show_hide_dates_start ) ) { | |
// ToDo: Maybe use code from set_date_prop() in woocommerce/includes/abstracts/abstract-wc-data.php. | |
$show_hide_dates_start = new WC_DateTime( date( 'Y-m-d 00:00:00', strtotime( $show_hide_dates_start ) ), new DateTimeZone( 'UTC' ) ); | |
// ToDo: If $show_hide_dates_start is in the future I should hide the product now! (set product_visibility terms)? | |
} | |
} | |
// Force 'date to' to the end of the day. | |
if ( isset( $_POST['_show_hide_dates_end'] ) ) { | |
$show_hide_dates_end = wc_clean( wp_unslash( $_POST['_show_hide_dates_end'] ) ); | |
if ( ! empty( $show_hide_dates_end ) ) { | |
// ToDo: Maybe use code from set_date_prop() in woocommerce/includes/abstracts/abstract-wc-data.php. | |
$show_hide_dates_end = new WC_DateTime( date( 'Y-m-d 00:00:00', strtotime( $show_hide_dates_end ) ), new DateTimeZone( 'UTC' ) ); | |
} | |
} | |
$product->update_meta_data( $this->show_hide_dates_start_key, $show_hide_dates_start ? $show_hide_dates_start->getTimestamp() : $show_hide_dates_start ); | |
$product->update_meta_data( $this->show_hide_dates_end_key, $show_hide_dates_end ? $show_hide_dates_end->getTimestamp() : $show_hide_dates_end ); | |
$product->save(); | |
} | |
// Returns an array of IDs of products that will be shown soon. | |
public function get_starting_shows() { | |
global $wpdb; | |
return $wpdb->get_col( | |
$wpdb->prepare( | |
"SELECT postmeta.post_id FROM {$wpdb->postmeta} as postmeta | |
WHERE postmeta.meta_key = '%s' | |
AND postmeta.meta_value > 0 | |
AND postmeta.meta_value < %s", | |
$this->show_hide_dates_start_key, time() | |
) | |
); | |
} | |
// Returns an array of IDs of products that are due to be hidden. | |
public function get_ending_shows() { | |
global $wpdb; | |
return $wpdb->get_col( | |
$wpdb->prepare( | |
"SELECT postmeta.post_id FROM {$wpdb->postmeta} as postmeta | |
WHERE postmeta.meta_key = '%s' | |
AND postmeta.meta_value > 0 | |
AND postmeta.meta_value < %s", | |
$this->show_hide_dates_end_key, time() | |
) | |
); | |
} | |
// Scheduled check for products to show or hide. | |
public function scheduled_show_hide_check() { | |
// Get products to be shown (or published). | |
$product_ids = $this->get_starting_shows(); | |
if ( $product_ids ) { | |
$visibility_ids = wc_get_product_visibility_term_ids(); | |
foreach ( $product_ids as $product_id ) { | |
$product = wc_get_product( $product_id ); | |
if ( $product ) { | |
$visibility_terms = $this->get_visibility_terms( $product_id ); | |
wp_set_object_terms( $product_id, $visibility_terms, 'product_visibility' ); | |
// Delete the start date so we don't keep changing the product_visibility terms. | |
$product->delete_meta_data( $this->show_hide_dates_start_key ); | |
$product->save(); | |
} | |
} | |
} | |
// Get products to be hidden (or unpublished). | |
$product_ids = $this->get_ending_shows(); | |
if ( $product_ids ) { | |
foreach ( $product_ids as $product_id ) { | |
$product = wc_get_product( $product_id ); | |
if ( $product ) { | |
// Get the terms without the 'exclude-from-catalog' or 'exclude-from-search' terms. | |
$visibility_terms = $this->get_visibility_terms( $product_id ); | |
// Add 'exclude-from-catalog' and 'exclude-from-search' terms so that the product will be hidden. | |
array_push( $visibility_terms, 'exclude-from-catalog', 'exclude-from-search' ); | |
// Hide product by adding 'exclude-from-catalog' and 'exclude-from-search' terms. | |
wp_set_object_terms( $product_id, $visibility_terms, 'product_visibility' ); | |
// Delete the end date so we don't keep hiding this product. | |
$product->delete_meta_data( $this->show_hide_dates_end_key ); | |
$product->save(); | |
} | |
} | |
} | |
} | |
} | |
$ScheduleHideProducts = new ScheduleHideProducts(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment