';
if ( ! $cache_enabled || ! $super_cache_enabled || true === defined( 'DISABLESUPERCACHEPRELOADING' ) ) {
@@ -17,6 +17,17 @@
$min_refresh_interval = wpsc_get_minimum_preload_interval();
+// Set defaults for preload scheduler variables if not set.
+if ( ! isset( $preload_schedule_type ) ) {
+ $preload_schedule_type = 'interval';
+}
+if ( ! isset( $preload_scheduled_time ) ) {
+ $preload_scheduled_time = '00:00';
+}
+if ( ! isset( $preload_schedule_interval ) ) {
+ $preload_schedule_interval = 'daily';
+}
+
echo '
';
-echo '
' . sprintf( __( 'Refresh preloaded cache files every %s minutes. (0 to disable, minimum %d minutes.)', 'wp-super-cache' ), " ", $min_refresh_interval ) . '
';
if ( $count > 100 ) {
$step = (int)( $count / 10 );
@@ -66,6 +76,43 @@
echo '
';
}
+// Preload Scheduler UI
+echo "';
+
+echo '
';
+
echo '
' . __( 'Preload mode (garbage collection disabled. Recommended.)', 'wp-super-cache' ) . '
';
diff --git a/wp-cache-config-sample.php b/wp-cache-config-sample.php
index 67b77b2d..34f6a08a 100644
--- a/wp-cache-config-sample.php
+++ b/wp-cache-config-sample.php
@@ -94,6 +94,9 @@
$wp_cache_preload_taxonomies = 0;
$wp_cache_preload_email_me = 0;
$wp_cache_preload_email_volume = 'none';
+$preload_schedule_type = 'interval'; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+$preload_scheduled_time = '00:00'; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+$preload_schedule_interval = 'daily'; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$wp_cache_mobile_prefixes = '';
$cached_direct_pages = array();
$wpsc_served_header = false;
diff --git a/wp-cache.php b/wp-cache.php
index 88c15585..c2723b73 100644
--- a/wp-cache.php
+++ b/wp-cache.php
@@ -1083,6 +1083,7 @@ function toggleLayer( whichLayer ) {
if ( 'preload' === $curr_tab ) {
if ( true == $super_cache_enabled && ! defined( 'DISABLESUPERCACHEPRELOADING' ) ) {
global $wp_cache_preload_interval, $wp_cache_preload_on, $wp_cache_preload_taxonomies, $wp_cache_preload_email_me, $wp_cache_preload_email_volume, $wp_cache_preload_posts, $wpdb;
+ global $preload_schedule_type, $preload_scheduled_time, $preload_schedule_interval; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Used in partials/preload.php.
wpsc_preload_settings();
$currently_preloading = false;
@@ -3689,11 +3690,42 @@ function wp_cron_preload_cache() {
} else {
$msg = '';
wpsc_reset_preload_counter();
- if ( (int)$wp_cache_preload_interval && defined( 'DOING_CRON' ) ) {
- if ( $wp_cache_preload_email_me )
- $msg = sprintf( __( 'Scheduling next preload refresh in %d minutes.', 'wp-super-cache' ), (int)$wp_cache_preload_interval );
- wp_cache_debug( "wp_cron_preload_cache: no more posts. scheduling next preload in $wp_cache_preload_interval minutes.", 5 );
- wp_schedule_single_event( time() + ( (int)$wp_cache_preload_interval * 60 ), 'wp_cache_full_preload_hook' );
+ global $preload_schedule_type, $preload_scheduled_time, $preload_schedule_interval;
+
+ // Set defaults if not set
+ if ( ! isset( $preload_schedule_type ) ) {
+ $preload_schedule_type = 'interval';
+ }
+
+ if ( defined( 'DOING_CRON' ) ) {
+ if ( $preload_schedule_type === 'interval' && (int) $wp_cache_preload_interval ) {
+ // Interval-based scheduling
+ if ( $wp_cache_preload_email_me ) {
+ // translators: %d is the number of minutes until the next preload refresh.
+ $msg = sprintf( __( 'Scheduling next preload refresh in %d minutes.', 'wp-super-cache' ), (int) $wp_cache_preload_interval );
+ }
+ wp_cache_debug( "wp_cron_preload_cache: no more posts. scheduling next preload in $wp_cache_preload_interval minutes.", 5 );
+ wp_schedule_single_event( time() + ( (int) $wp_cache_preload_interval * 60 ), 'wp_cache_full_preload_hook' );
+ } elseif ( $preload_schedule_type === 'time' ) {
+ // Time-based scheduling - the event will already be scheduled as recurring
+ // Check if already scheduled, if not schedule it
+ if ( ! wp_next_scheduled( 'wp_cache_full_preload_hook' ) ) {
+ if ( ! isset( $preload_scheduled_time ) ) {
+ $preload_scheduled_time = '00:00';
+ }
+ if ( ! isset( $preload_schedule_interval ) ) {
+ $preload_schedule_interval = 'daily';
+ }
+ $schedules = wp_get_schedules();
+ $interval_display = isset( $schedules[ $preload_schedule_interval ]['display'] ) ? $schedules[ $preload_schedule_interval ]['display'] : $preload_schedule_interval;
+ if ( $wp_cache_preload_email_me ) {
+ /* translators: 1: scheduled time, 2: schedule interval display name */
+ $msg = sprintf( __( 'Scheduling next preload at %1$s (%2$s).', 'wp-super-cache' ), $preload_scheduled_time, $interval_display );
+ }
+ wp_cache_debug( "wp_cron_preload_cache: no more posts. scheduling next preload at $preload_scheduled_time ($preload_schedule_interval).", 5 );
+ wp_schedule_event( wpsc_next_scheduled_preload_timestamp( $preload_scheduled_time ), $preload_schedule_interval, 'wp_cache_full_preload_hook' );
+ }
+ }
}
global $file_prefix, $cache_max_time;
if ( $wp_cache_preload_interval > 0 ) {
@@ -4022,8 +4054,37 @@ function wpsc_get_minimum_preload_interval() {
return apply_filters( 'wpsc_minimum_preload_interval', 10 );
}
+/**
+ * Convert an "HH:MM" clock time (interpreted in the site's timezone) into the
+ * next UTC timestamp at or after "now". Falls back to "00:00" for malformed
+ * input.
+ *
+ * @param string $hhmm Wall-clock time in the site's timezone, e.g. "03:00".
+ * @return int UTC timestamp suitable for wp_schedule_event().
+ */
+function wpsc_next_scheduled_preload_timestamp( $hhmm ) {
+ if ( ! preg_match( '/^(?:[01][0-9]|2[0-3]):[0-5][0-9]$/', (string) $hhmm ) ) {
+ $hhmm = '00:00';
+ }
+
+ $site_tz = function_exists( 'wp_timezone' ) ? wp_timezone() : new DateTimeZone( 'UTC' );
+ $dt = DateTime::createFromFormat( 'H:i', $hhmm, $site_tz );
+
+ if ( ! $dt ) {
+ $dt = new DateTime( 'now', $site_tz );
+ $dt->setTime( (int) substr( $hhmm, 0, 2 ), (int) substr( $hhmm, 3, 2 ), 0 );
+ }
+
+ if ( $dt->getTimestamp() <= time() ) {
+ $dt->modify( '+1 day' );
+ }
+
+ return $dt->getTimestamp();
+}
+
function wpsc_preload_settings() {
global $wp_cache_preload_interval, $wp_cache_preload_on, $wp_cache_preload_taxonomies, $wp_cache_preload_email_volume, $wp_cache_preload_posts, $valid_nonce;
+ global $preload_schedule_type, $preload_scheduled_time, $preload_schedule_interval;
if ( isset( $_POST[ 'action' ] ) == false || $_POST[ 'action' ] != 'preload' )
return;
@@ -4051,6 +4112,42 @@ function wpsc_preload_settings() {
// Set to true if the preload interval is changed, and a reschedule is required.
$force_preload_reschedule = false;
+ // Handle preload schedule type (interval vs time)
+ // Nonce is verified in wp_cache_manager() before this function is called.
+ if ( isset( $_POST['preload_schedule_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $new_schedule_type = sanitize_text_field( wp_unslash( $_POST['preload_schedule_type'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $new_schedule_type = in_array( $new_schedule_type, array( 'interval', 'time' ), true ) ? $new_schedule_type : 'interval';
+ if ( ! isset( $preload_schedule_type ) || $preload_schedule_type !== $new_schedule_type ) {
+ $force_preload_reschedule = true;
+ }
+ $preload_schedule_type = $new_schedule_type;
+ wp_cache_setting( 'preload_schedule_type', $preload_schedule_type );
+ }
+
+ if ( isset( $_POST['preload_scheduled_time'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $new_scheduled_time = sanitize_text_field( wp_unslash( $_POST['preload_scheduled_time'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ if ( ! preg_match( '/^(?:[01][0-9]|2[0-3]):[0-5][0-9]$/', $new_scheduled_time ) ) {
+ $new_scheduled_time = '00:00';
+ }
+ if ( ! isset( $preload_scheduled_time ) || $preload_scheduled_time !== $new_scheduled_time ) {
+ $force_preload_reschedule = true;
+ }
+ $preload_scheduled_time = $new_scheduled_time;
+ wp_cache_setting( 'preload_scheduled_time', $preload_scheduled_time );
+ }
+
+ // Handle preload schedule interval (hourly, twicedaily, daily, etc.)
+ if ( isset( $_POST['preload_schedule_interval'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $schedules = wp_get_schedules();
+ $new_schedule_interval = sanitize_text_field( wp_unslash( $_POST['preload_schedule_interval'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $new_schedule_interval = isset( $schedules[ $new_schedule_interval ] ) ? $new_schedule_interval : 'daily';
+ if ( ! isset( $preload_schedule_interval ) || $preload_schedule_interval !== $new_schedule_interval ) {
+ $force_preload_reschedule = true;
+ }
+ $preload_schedule_interval = $new_schedule_interval;
+ wp_cache_setting( 'preload_schedule_interval', $preload_schedule_interval );
+ }
+
if ( isset( $_POST[ 'wp_cache_preload_interval' ] ) && ( $_POST[ 'wp_cache_preload_interval' ] == 0 || $_POST[ 'wp_cache_preload_interval' ] >= $min_refresh_interval ) ) {
$_POST[ 'wp_cache_preload_interval' ] = (int)$_POST[ 'wp_cache_preload_interval' ];
if ( $wp_cache_preload_interval != $_POST[ 'wp_cache_preload_interval' ] ) {
@@ -4095,9 +4192,21 @@ function wpsc_preload_settings() {
}
wp_cache_setting( 'wp_cache_preload_on', $wp_cache_preload_on );
+ // Set defaults if not set
+ if ( ! isset( $preload_schedule_type ) ) {
+ $preload_schedule_type = 'interval';
+ }
+
// Ensure that preload settings are applied to scheduled cron.
- $next_preload = wp_next_scheduled( 'wp_cache_full_preload_hook' );
- $should_schedule = ( $wp_cache_preload_on === 1 && $wp_cache_preload_interval > 0 );
+ $next_preload = wp_next_scheduled( 'wp_cache_full_preload_hook' );
+
+ // Determine if we should schedule based on schedule type
+ if ( $preload_schedule_type === 'interval' ) {
+ $should_schedule = ( $wp_cache_preload_on === 1 && $wp_cache_preload_interval > 0 );
+ } else {
+ // For time-based scheduling, we schedule if preload mode is on
+ $should_schedule = ( $wp_cache_preload_on === 1 );
+ }
// If forcing a reschedule, or preload is disabled, clear the next scheduled event.
if ( $next_preload && ( ! $should_schedule || $force_preload_reschedule ) ) {
@@ -4105,6 +4214,7 @@ function wpsc_preload_settings() {
wpsc_reset_preload_counter();
wpsc_create_stop_preload_flag();
wp_unschedule_event( $next_preload, 'wp_cache_full_preload_hook' );
+ wp_clear_scheduled_hook( 'wp_cache_full_preload_hook' );
$next_preload = 0;
}
@@ -4112,7 +4222,20 @@ function wpsc_preload_settings() {
// Ensure a preload is scheduled if it should be.
if ( ! $next_preload && $should_schedule ) {
wp_cache_debug( 'Scheduling new preload event' );
- wp_schedule_single_event( time() + ( $wp_cache_preload_interval * 60 ), 'wp_cache_full_preload_hook' );
+
+ if ( $preload_schedule_type === 'interval' ) {
+ // Interval-based: schedule single event X minutes from now
+ wp_schedule_single_event( time() + ( $wp_cache_preload_interval * 60 ), 'wp_cache_full_preload_hook' );
+ } else {
+ // Time-based: schedule recurring event at specific time
+ if ( ! isset( $preload_scheduled_time ) ) {
+ $preload_scheduled_time = '00:00';
+ }
+ if ( ! isset( $preload_schedule_interval ) ) {
+ $preload_schedule_interval = 'daily';
+ }
+ wp_schedule_event( wpsc_next_scheduled_preload_timestamp( $preload_scheduled_time ), $preload_schedule_interval, 'wp_cache_full_preload_hook' );
+ }
}
}