'string', 'single' => true, 'show_in_rest' => true, 'auth_callback' => '__return_false']); } if (! wp_next_scheduled('pcwp_refresh_next_dates')) { wp_schedule_event(time() + HOUR_IN_SECONDS, 'hourly', 'pcwp_refresh_next_dates'); } $cursor = (int) get_option('pcwp_next_dates_cursor', 0); if (get_option('pcwp_next_dates_version') !== '1' && ! wp_next_scheduled('pcwp_backfill_next_dates', [$cursor])) { wp_schedule_single_event(time() + 10, 'pcwp_backfill_next_dates', [$cursor]); } }); add_action('pcwp_refresh_next_dates', [$this, 'refresh']); add_action('pcwp_series_propagation_end', [$this, 'refresh']); add_action('pcwp_occurrence_synced', [$this, 'event']); add_action('pcwp_backfill_next_dates', [$this, 'backfill']); add_action('pcwp_refresh_series_dates', [$this, 'seriesBatch']); add_action('pcwp_refresh_event_dates', [$this, 'eventBatch']); } public function backfill(int $after = 0): void { global $wpdb; $ids = $wpdb->get_col($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE post_type='pcwp_event' AND ID>%d ORDER BY ID LIMIT 50", $after)); foreach ($ids as $id) { $this->event((int) $id); } if (count($ids) === 50) { update_option('pcwp_next_dates_cursor', (int) end($ids), false); wp_schedule_single_event(time() + 10, 'pcwp_backfill_next_dates', [(int) end($ids)]); } else { $this->refresh(); update_option('pcwp_next_dates_version', '1', false); delete_option('pcwp_next_dates_cursor'); } } public function event(int $id): void { $event = (new WpEventRepository())->find($id); if ($event === null || get_post_status($id) !== 'publish' || in_array($event->status, ['canceled', 'postponed'], true)) { delete_post_meta($id, '_pcwp_next_start_utc'); return; } try { $times = ($event->schedule['type'] ?? '') === 'daily' ? array_column(DailySchedule::intervals($event->schedule, $event->timezone), 'time') : [new EventTime($event->startLocal, $event->endLocal, $event->timezone)]; } catch (\InvalidArgumentException) { delete_post_meta($id, '_pcwp_next_start_utc'); return; } foreach ($times as $time) { if ($time->endUtc()->getTimestamp() >= time()) { update_post_meta($id, '_pcwp_next_start_utc', $time->startUtc()->format('Y-m-d H:i:s')); return; } } delete_post_meta($id, '_pcwp_next_start_utc'); } public function refresh(int $seriesId = 0): void { if ($seriesId === 0) { $this->seriesBatch(); $this->eventBatch(); return; } global $wpdb; $seriesIds = [$seriesId]; foreach ($seriesIds as $id) { $next = $wpdb->get_var($wpdb->prepare("SELECT MIN(o.start_utc) FROM {$wpdb->prefix}pcwp_occurrences o INNER JOIN {$wpdb->posts} p ON p.ID=o.event_id WHERE o.series_id=%d AND o.end_utc>=UTC_TIMESTAMP() AND p.post_status='publish' AND o.event_status NOT IN ('canceled','postponed')", $id)); if ($next) { update_post_meta((int) $id, '_pcwp_next_start_utc', $next); } else { delete_post_meta((int) $id, '_pcwp_next_start_utc'); } } } public function seriesBatch(int $after = 0): void { global $wpdb; $ids = $wpdb->get_col($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE post_type='pcwp_series' AND ID>%d ORDER BY ID LIMIT 50", $after)); foreach ($ids as $id) { $this->refresh((int) $id); } if (count($ids) === 50) { wp_schedule_single_event(time() + 10, 'pcwp_refresh_series_dates', [(int) end($ids)]); } } public function eventBatch(int $after = 0): void { global $wpdb; $ids = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT pm.post_id FROM {$wpdb->postmeta} pm INNER JOIN {$wpdb->posts} p ON p.ID=pm.post_id WHERE p.post_type='pcwp_event' AND pm.meta_key='_pcwp_next_start_utc' AND pm.meta_value%d ORDER BY pm.post_id LIMIT 50", $after)); foreach ($ids as $id) { $this->event((int) $id); } if (count($ids) === 50) { wp_schedule_single_event(time() + 10, 'pcwp_refresh_event_dates', [(int) end($ids)]); } } }