Skip to content

Commit 67538f0

Browse files
authored
Merge pull request #1028 from Codeinwp/feat/pro/572
Added cron schedule settings per import job
2 parents 9b379bd + 5eb0053 commit 67538f0

File tree

5 files changed

+243
-17
lines changed

5 files changed

+243
-17
lines changed

includes/admin/feedzy-rss-feeds-import.php

Lines changed: 111 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,21 @@ public function feedzy_import_feed_options() {
406406
$default_thumbnail_id = ! empty( $this->free_settings['general']['default-thumbnail-id'] ) ? (int) $this->free_settings['general']['default-thumbnail-id'] : 0;
407407
}
408408
}
409+
$import_schedule = array(
410+
'fz_execution_offset' => ! empty( $this->free_settings['general']['fz_execution_offset'] ) ? $this->free_settings['general']['fz_execution_offset'] : '',
411+
'fz_cron_execution' => ! empty( $this->free_settings['general']['fz_cron_execution'] ) ? $this->free_settings['general']['fz_cron_execution'] : '',
412+
'fz_cron_schedule' => ! empty( $this->free_settings['general']['fz_cron_schedule'] ) ? $this->free_settings['general']['fz_cron_schedule'] : '',
413+
);
414+
415+
$fz_cron_execution = get_post_meta( $post->ID, 'fz_cron_execution', true );
416+
$fz_cron_schedule = get_post_meta( $post->ID, 'fz_cron_schedule', true );
417+
$fz_execution_offset = get_post_meta( $post->ID, 'fz_execution_offset', true );
418+
if ( ! empty( $fz_cron_schedule ) && ! empty( $fz_cron_execution ) ) {
419+
$import_schedule['fz_cron_schedule'] = $fz_cron_schedule;
420+
$import_schedule['fz_execution_offset'] = $fz_execution_offset;
421+
$import_schedule['fz_cron_execution'] = $fz_cron_execution;
422+
}
423+
409424
$post_status = $post->post_status;
410425
$nonce = wp_create_nonce( FEEDZY_BASEFILE );
411426
$invalid_source_msg = apply_filters( 'feedzy_get_source_validity_error', '', $post );
@@ -473,6 +488,20 @@ public function save_feedzy_import_feed_meta( $post_id, $post ) {
473488
}
474489
}
475490
}
491+
492+
$global_cron_execution = ! empty( $this->free_settings['general']['fz_cron_execution'] ) ? $this->free_settings['general']['fz_cron_execution'] : '';
493+
$global_cron_schedule = ! empty( $this->free_settings['general']['fz_cron_schedule'] ) ? $this->free_settings['general']['fz_cron_schedule'] : '';
494+
if (
495+
(
496+
empty( $data_meta['fz_cron_execution'] ) || $global_cron_schedule === $data_meta['fz_cron_execution']
497+
)
498+
&&
499+
empty( $data_meta['fz_cron_schedule'] ) || $global_cron_schedule === $data_meta['fz_cron_schedule']
500+
) {
501+
// Remove scheduled cron settings if they are equal to the global settings.
502+
unset( $data_meta['fz_cron_execution'], $data_meta['fz_cron_schedule'], $data_meta['fz_execution_offset'] );
503+
}
504+
476505
$custom_fields_keys = array();
477506
if ( isset( $_POST['custom_vars_key'] ) && is_array( $_POST['custom_vars_key'] ) ) {
478507
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
@@ -543,7 +572,8 @@ public function save_feedzy_import_feed_meta( $post_id, $post ) {
543572
wp_update_post( $activate );
544573
add_action( 'save_post_feedzy_imports', array( $this, 'save_feedzy_import_feed_meta' ), 1, 2 );
545574
}
546-
575+
// Clear the import job cron schedule if it exists.
576+
Feedzy_Rss_Feeds_Util_Scheduler::clear_scheduled_hook( 'feedzy_cron', array( 100, $post_id ) );
547577
do_action( 'feedzy_save_fields', $post_id, $post );
548578
}
549579

@@ -724,20 +754,33 @@ public function manage_feedzy_import_columns( $column, $post_id ) {
724754

725755
break;
726756
case 'feedzy-next_run':
727-
$next = wp_next_scheduled( 'feedzy_cron' );
757+
$next = Feedzy_Rss_Feeds_Util_Scheduler::is_scheduled( 'feedzy_cron', array( 100, $post_id ) );
758+
if ( ! $next ) {
759+
$next = Feedzy_Rss_Feeds_Util_Scheduler::is_scheduled( 'feedzy_cron' );
760+
}
728761
if ( $next ) {
729762
$now = new DateTime();
730763
$then = new DateTime();
731764
$then = $then->setTimestamp( $next );
732765
$in = $now->diff( $then );
733-
echo wp_kses_post(
734-
sprintf(
735-
// translators: %1$d: number of hours, %2$d: number of minutes
736-
__( 'In %1$d hours %2$d minutes', 'feedzy-rss-feeds' ),
737-
$in->format( '%h' ),
738-
$in->format( '%i' )
739-
)
740-
);
766+
767+
$time_string = array();
768+
// Add days if they exist.
769+
if ( $in->d > 0 ) {
770+
// translators: %1$s days.
771+
$time_string[] = sprintf( __( '%1$d days', 'feedzy-rss-feeds' ), $in->d );
772+
}
773+
// Add hours if they exist.
774+
if ( $in->h > 0 ) {
775+
// translators: %1$s hours.
776+
$time_string[] = sprintf( __( '%1$d hours', 'feedzy-rss-feeds' ), $in->h );
777+
}
778+
// Add minutes if they exist.
779+
if ( $in->i > 0 ) {
780+
// translators: %1$s minutes.
781+
$time_string[] = sprintf( __( '%1$d minutes', 'feedzy-rss-feeds' ), $in->i );
782+
}
783+
echo wp_kses_post( join( ' ', $time_string ) );
741784
}
742785
break;
743786
default:
@@ -1223,7 +1266,7 @@ function ( $errors, $feed, $url ) {
12231266
* @since 1.2.0
12241267
* @access public
12251268
*/
1226-
public function run_cron( $max = 100 ) {
1269+
public function run_cron( $max = 100, $job_id = 0 ) {
12271270
if ( empty( $max ) ) {
12281271
$max = 10;
12291272
}
@@ -1234,9 +1277,26 @@ public function run_cron( $max = 100 ) {
12341277
'post_type' => 'feedzy_imports',
12351278
'post_status' => 'publish',
12361279
'numberposts' => 99,
1280+
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
1281+
'meta_query' => array(
1282+
'relation' => 'AND',
1283+
array(
1284+
'key' => 'fz_cron_execution',
1285+
'compare' => 'NOT EXISTS',
1286+
),
1287+
array(
1288+
'key' => 'fz_cron_schedule',
1289+
'compare' => 'NOT EXISTS',
1290+
),
1291+
),
12371292
)
12381293
);
12391294

1295+
if ( $job_id ) {
1296+
$args['post__in'] = array( $job_id );
1297+
unset( $args['meta_query'], $args['numberposts'] );
1298+
}
1299+
12401300
$feedzy_imports = get_posts( $args );
12411301
foreach ( $feedzy_imports as $job ) {
12421302
try {
@@ -2266,11 +2326,46 @@ public function add_cron() {
22662326
$offset = sanitize_text_field( wp_unslash( $_POST['fz_execution_offset'] ) );
22672327
$time = $this->get_cron_execution( $execution, $offset );
22682328
$schedule = sanitize_text_field( wp_unslash( $_POST['fz_cron_schedule'] ) );
2269-
wp_clear_scheduled_hook( 'feedzy_cron' );
2329+
Feedzy_Rss_Feeds_Util_Scheduler::clear_scheduled_hook( 'feedzy_cron' );
22702330
}
22712331
}
2272-
if ( false === wp_next_scheduled( 'feedzy_cron' ) ) {
2273-
wp_schedule_event( $time, $schedule, 'feedzy_cron' );
2332+
if ( false === Feedzy_Rss_Feeds_Util_Scheduler::is_scheduled( 'feedzy_cron' ) ) {
2333+
Feedzy_Rss_Feeds_Util_Scheduler::schedule_event( $time, $schedule, 'feedzy_cron' );
2334+
}
2335+
2336+
// Register import jobs based cron jobs.
2337+
$import_job_crons = get_posts(
2338+
array(
2339+
'post_type' => 'feedzy_imports',
2340+
'post_status' => 'publish',
2341+
'numberposts' => 99,
2342+
'fields' => 'ids',
2343+
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
2344+
'meta_query' => array(
2345+
'relation' => 'AND',
2346+
array(
2347+
'key' => 'fz_cron_execution',
2348+
'compare' => 'EXISTS',
2349+
),
2350+
array(
2351+
'key' => 'fz_cron_schedule',
2352+
'compare' => 'EXISTS',
2353+
),
2354+
),
2355+
)
2356+
);
2357+
2358+
if ( ! empty( $import_job_crons ) ) {
2359+
foreach ( $import_job_crons as $job_id ) {
2360+
$fz_cron_execution = get_post_meta( $job_id, 'fz_cron_execution', true );
2361+
$fz_cron_schedule = get_post_meta( $job_id, 'fz_cron_schedule', true );
2362+
$fz_execution_offset = get_post_meta( $job_id, 'fz_execution_offset', true );
2363+
$time = $this->get_cron_execution( $fz_cron_execution, $fz_execution_offset );
2364+
2365+
if ( false === Feedzy_Rss_Feeds_Util_Scheduler::is_scheduled( 'feedzy_cron', array( 100, $job_id ) ) ) {
2366+
Feedzy_Rss_Feeds_Util_Scheduler::schedule_event( $time, $fz_cron_schedule, 'feedzy_cron', array( 100, $job_id ) );
2367+
}
2368+
}
22742369
}
22752370
}
22762371

@@ -2285,7 +2380,7 @@ public function get_cron_execution( $execution, $offset = 0 ) {
22852380
if ( empty( $offset ) && ! empty( $this->free_settings['general']['fz_execution_offset'] ) ) {
22862381
$offset = $this->free_settings['general']['fz_execution_offset'];
22872382
}
2288-
$execution = strtotime( $execution ) ? strtotime( $execution ) + ( HOUR_IN_SECONDS * $offset ) : time() + ( HOUR_IN_SECONDS * $offset );
2383+
$execution = strtotime( $execution ) ? strtotime( $execution ) + ( HOUR_IN_SECONDS * (int) $offset ) : time() + ( HOUR_IN_SECONDS * (int) $offset );
22892384
return $execution;
22902385
}
22912386

@@ -2306,7 +2401,7 @@ public function admin_notices() {
23062401
echo wp_kses_post( '<div class="notice notice-error feedzy-error-critical is-dismissible"><p>' . __( 'WP Cron is disabled. Your feeds would not get updated. Please contact your hosting provider or system administrator', 'feedzy-rss-feeds' ) . '</p></div>' );
23072402
}
23082403

2309-
if ( false === wp_next_scheduled( 'feedzy_cron' ) ) {
2404+
if ( false === Feedzy_Rss_Feeds_Util_Scheduler::is_scheduled( 'feedzy_cron' ) ) {
23102405
echo wp_kses_post( '<div class="notice notice-error"><p>' . __( 'Unable to register cron job. Your feeds might not get updated', 'feedzy-rss-feeds' ) . '</p></div>' );
23112406
}
23122407
}

includes/feedzy-rss-feeds.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ function () {
229229
self::$instance->loader->add_action( 'admin_enqueue_scripts', $plugin_import, 'enqueue_styles' );
230230
self::$instance->loader->add_action( 'init', $plugin_import, 'register_import_post_type', 9, 1 );
231231
self::$instance->loader->add_action( 'add_meta_boxes', $plugin_import, 'add_feedzy_import_metaboxes', 1, 2 );
232-
self::$instance->loader->add_action( 'feedzy_cron', $plugin_import, 'run_cron' );
232+
self::$instance->loader->add_action( 'feedzy_cron', $plugin_import, 'run_cron', 10, 2 );
233233
self::$instance->loader->add_action( 'save_post_feedzy_imports', $plugin_import, 'save_feedzy_import_feed_meta', 1, 2 );
234234
self::$instance->loader->add_action( 'wp_ajax_feedzy', $plugin_import, 'ajax' );
235235
self::$instance->loader->add_action( 'manage_feedzy_imports_posts_custom_column', $plugin_import, 'manage_feedzy_import_columns', 10, 2 );
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
/**
3+
* The class that contains utility functions for scheduling tasks.
4+
*
5+
* @link https://themeisle.com
6+
*
7+
* @package feedzy-rss-feeds
8+
* @subpackage feedzy-rss-feeds/includes/util
9+
*/
10+
11+
/**
12+
* The class that contains utility functions for scheduling tasks.
13+
*
14+
* Class that contains utility functions for scheduling tasks.
15+
*
16+
* @package feedzy-rss-feeds
17+
* @subpackage feedzy-rss-feeds/includes/util
18+
* @author Themeisle <[email protected]>
19+
*/
20+
class Feedzy_Rss_Feeds_Util_Scheduler {
21+
22+
/**
23+
* Check if an action hook is scheduled.
24+
*
25+
* @param string $hook The hook to check.
26+
* @param array $args Optional. Arguments to pass to the hook
27+
*
28+
* @return bool
29+
*/
30+
public static function is_scheduled( $hook, $args = array() ) {
31+
if ( function_exists( 'as_has_scheduled_action' ) ) {
32+
return as_has_scheduled_action( $hook, $args );
33+
}
34+
35+
if ( function_exists( 'as_next_scheduled_action' ) ) {
36+
// For older versions of AS.
37+
return as_next_scheduled_action( $hook, $args ) !== false;
38+
}
39+
40+
return wp_next_scheduled( $hook, $args ) !== false;
41+
}
42+
43+
/**
44+
* Clear scheduled hook.
45+
*
46+
* @param string $hook The name of the hook to clear.
47+
* @param array $args Optional. Arguments that were to be passed to the hook's callback function. Default empty array.
48+
* @return mixed The scheduled action ID if a scheduled action was found, or null if no matching action found. If WP_Cron is used, on success an integer indicating number of events unscheduled, false or WP_Error if unscheduling one or more events fail.
49+
*/
50+
public static function clear_scheduled_hook( $hook, $args = array() ) {
51+
if ( function_exists( 'as_unschedule_all_actions' ) ) {
52+
return as_unschedule_all_actions( $hook, $args );
53+
}
54+
55+
return wp_clear_scheduled_hook( $hook, $args );
56+
}
57+
58+
/**
59+
* Schedule an event.
60+
*
61+
* @param int $time The first time that the event will occur.
62+
* @param string $recurrence How often the event should recur. See wp_get_schedules() for accepted values.
63+
* @param string $hook The name of the hook that will be triggered by the event.
64+
* @param array $args Optional. Arguments to pass to the hook's callback function. Default empty array.
65+
* @return integer|bool|WP_Error The action ID if Action Scheduler is used. True if event successfully scheduled, False or WP_Error on failure if WP Cron is used.
66+
*/
67+
public static function schedule_event( $time, $recurrence, $hook, $args = array() ) {
68+
if ( function_exists( 'as_schedule_recurring_action' ) ) {
69+
$schedules = wp_get_schedules();
70+
if ( isset( $schedules[ $recurrence ] ) ) {
71+
$interval = $schedules[ $recurrence ]['interval'];
72+
return as_schedule_recurring_action( $time, $interval, $hook, $args );
73+
}
74+
}
75+
76+
return wp_schedule_event( $time, $recurrence, $hook, $args );
77+
}
78+
}

includes/views/import-metabox-edit.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,55 @@ class="fz-switch-toggle" type="checkbox" value="yes"
835835
</div>
836836
</div>
837837
</div>
838+
<div class="form-block form-block-two-column <?php echo esc_attr( apply_filters( 'feedzy_upsell_class', '' ) ); ?>">
839+
<?php echo wp_kses_post( apply_filters( 'feedzy_upsell_content', '', 'schedule-import-job', 'import' ) ); ?>
840+
<div class="fz-left">
841+
<h4 class="h4"><?php esc_html_e( 'Schedule Import Job', 'feedzy-rss-feeds' ); ?> <?php echo ! feedzy_is_pro() ? ' <span class="pro-label">PRO</span>' : ''; ?></h4>
842+
</div>
843+
<div class="fz-right">
844+
<div class="fz-form-row">
845+
<div class="fz-form-col-6">
846+
<div class="fz-form-group">
847+
<label class="form-label"><?php esc_html_e( 'First cron execution time', 'feedzy-rss-feeds' ); ?></label>
848+
<?php if ( feedzy_is_pro() ) : ?>
849+
<input type="hidden" name="feedzy_meta_data[fz_execution_offset]" id="fz-execution-offset" value="<?php echo ! empty( $import_schedule['fz_execution_offset'] ) ? esc_attr( $import_schedule['fz_execution_offset'] ) : ''; ?>">
850+
<?php endif; ?>
851+
<input type="datetime-local" id="fz-event-execution" name="feedzy_meta_data[fz_cron_execution]" class="form-control" value="<?php echo ! empty( $import_schedule['fz_cron_execution'] ) ? esc_attr( $import_schedule['fz_cron_execution'] ) : ''; ?>"<?php disabled( true, ! feedzy_is_pro() ); ?>>
852+
<div class="help-text pt-8">
853+
<?php esc_html_e( 'When past date will be provided, event will be executed in the next queue.', 'feedzy-rss-feeds' ); ?>
854+
<a href="<?php echo esc_url( 'https://docs.themeisle.com/article/1820-how-to-set-scheduler-for-import-cron-jobs-in-feedzy' ); ?>" target="_blank"><?php esc_html_e( 'Learn More', 'feedzy-rss-feeds' ); ?></a>
855+
</div>
856+
</div>
857+
</div>
858+
<div class="fz-form-col-6">
859+
<div class="fz-form-group">
860+
<label class="form-label"><?php esc_html_e( 'Schedule', 'feedzy-rss-feeds' ); ?></label>
861+
<select id="fz-event-schedule" class="form-control fz-select-control" name="feedzy_meta_data[fz_cron_schedule]"<?php disabled( true, ! feedzy_is_pro() ); ?>>
862+
<?php
863+
$save_schedule = ! empty( $import_schedule['fz_cron_schedule'] ) ? $import_schedule['fz_cron_schedule'] : '';
864+
865+
$schedules = wp_get_schedules();
866+
if ( isset( $schedules['hourly'] ) ) {
867+
$hourly = $schedules['hourly'];
868+
unset( $schedules['hourly'] );
869+
$schedules = array_merge( array( 'hourly' => $hourly ), $schedules );
870+
}
871+
$duplicate_schedule = array();
872+
foreach ( $schedules as $slug => $schedule ) :
873+
if ( empty( $schedule['interval'] ) || in_array( $schedule['interval'], $duplicate_schedule, true ) ) {
874+
continue;
875+
}
876+
$duplicate_schedule[] = $schedule['interval'];
877+
?>
878+
<option value="<?php echo esc_attr( $slug ); ?>"<?php selected( $save_schedule, $slug ); ?>><?php echo esc_html( $schedule['display'] ); ?> (<?php echo esc_html( $slug ); ?>)</option>
879+
<?php endforeach; ?>
880+
</select>
881+
<div class="help-text pt-8"><?php esc_html_e( 'After first execution repeat.', 'feedzy-rss-feeds' ); ?></div>
882+
</div>
883+
</div>
884+
</div>
885+
</div>
886+
</div>
838887
<?php if ( function_exists( 'icl_get_languages' ) ) : ?>
839888
<div class="form-block form-block-two-column">
840889
<div class="fz-left">

tests/e2e/specs/upsell.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,9 @@ test.describe( 'Upsell', () => {
6565
await page.locator('.fz-form-group:has( #feed-post-default-thumbnail )').hover({ force: true });
6666
upgradeAlert = page.locator('#feedzy-import-form a[href*="utm_campaign=fallback-image"]');
6767
await expect( upgradeAlert ).toBeVisible();
68+
69+
await page.locator('.fz-form-group:has( #fz-event-execution )').hover({ force: true });
70+
upgradeAlert = page.locator('#feedzy-import-form a[href*="utm_campaign=schedule-import-job"]');
71+
await expect( upgradeAlert ).toBeVisible();
6872
} );
6973
});

0 commit comments

Comments
 (0)