is_block_editor()) { add_meta_box('pcwp_recurrence', __('Repeat schedule', 'proeventswp'), [$this, 'classic'], 'pcwp_series', 'normal', 'high'); } }); add_action('add_meta_boxes_pcwp_event', function (): void { if (! get_current_screen()?->is_block_editor()) { add_meta_box('pcwp_repeat_event', __('Repeat schedule', 'proeventswp'), [$this, 'classicEvent'], 'pcwp_event', 'normal', 'default'); } }); add_action('save_post_pcwp_series', [$this, 'save'], 20, 2); add_action('admin_notices', [$this, 'notice']); add_action('admin_post_pcwp_review_cancelled_date', function (): void { $id = absint($_POST['event_id'] ?? 0); check_admin_referer('pcwp_review_cancelled_' . $id); if (get_post_type($id) !== 'pcwp_event' || ! current_user_can('edit_post', $id)) { wp_die(esc_html__('You cannot review this event.', 'proeventswp')); } delete_post_meta($id, '_pcwp_recurrence_attention'); wp_safe_redirect(get_edit_post_link($id, 'raw')); exit; }); } public function assets(): void { $screen = get_current_screen(); if (! in_array($screen?->post_type, ['pcwp_event', 'pcwp_series'], true)) { return; } wp_enqueue_script('pcwp-series-editor', PCWP_URL . 'assets/series-editor.js', ['pcwp-entity-editor', 'wp-components', 'wp-data', 'wp-edit-post', 'wp-element', 'wp-i18n', 'wp-plugins', 'wp-api-fetch'], (string) filemtime(PCWP_PATH . 'assets/series-editor.js'), true); wp_enqueue_style('pcwp-authoring', PCWP_URL . 'assets/authoring.css', ['wp-components'], (string) filemtime(PCWP_PATH . 'assets/authoring.css')); $id = absint($_GET['post'] ?? 0); $seriesId = $screen->post_type === 'pcwp_series' ? $id : absint(get_post_meta($id, '_pcwp_series_id', true)); wp_localize_script('pcwp-series-editor', 'pcwpRecurrence', ['timezones' => timezone_identifiers_list(), 'classic' => ! $screen->is_block_editor(), 'review' => get_post_meta($seriesId, '_pcwp_recurrence_review', true)]); wp_enqueue_script('pcwp-entity-editor', PCWP_URL . 'assets/entity-editor.js', ['wp-components', 'wp-element', 'wp-api-fetch', 'wp-dom-ready'], (string) filemtime(PCWP_PATH . 'assets/entity-editor.js'), true); } public function classic(\WP_Post $post): void { wp_nonce_field('pcwp_series_' . $post->ID, 'pcwp_series_nonce'); echo '
'; foreach (['_pcwp_start' => __('Starts', 'proeventswp'), '_pcwp_end' => __('Ends', 'proeventswp'), '_pcwp_timezone' => __('Timezone', 'proeventswp'), SeriesMeta::RULE => __('Advanced RRULE', 'proeventswp')] as $key => $label) { echo ''; } foreach ([SeriesMeta::EXCLUSIONS => __('Skipped dates, one per line', 'proeventswp'), SeriesMeta::ADDITIONS => __('Additional dates, one per line', 'proeventswp')] as $key => $label) { echo ''; } foreach (['_pcwp_venue_ids', '_pcwp_organizer_ids'] as $key) { echo ''; } echo ''; } public function classicEvent(\WP_Post $post): void { $meta = []; foreach (['_pcwp_start', '_pcwp_end', '_pcwp_timezone', '_pcwp_series_id', '_pcwp_series_overrides'] as $key) { $meta[$key] = get_post_meta($post->ID, $key, true); } if ((get_post_meta($post->ID, '_pcwp_schedule', true)['type'] ?? '') === 'daily') { echo '' . esc_html__('Daily opening-hours schedules do not repeat.', 'proeventswp') . '
'; return; } echo '' . esc_html__('Save the event details before creating or updating its repeat schedule.', 'proeventswp') . '
'; } public function save(int $id, \WP_Post $post): void { unset($post); if (! current_user_can('edit_post', $id) || wp_is_post_revision($id) || ! isset($_POST['pcwp_series_nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['pcwp_series_nonce'])), 'pcwp_series_' . $id)) { return; } foreach (['_pcwp_start', '_pcwp_end', '_pcwp_timezone', SeriesMeta::RULE] as $key) { if (isset($_POST[$key])) { update_post_meta($id, $key, sanitize_text_field(wp_unslash($_POST[$key]))); } } update_post_meta($id, '_pcwp_all_day', ! empty($_POST['_pcwp_all_day'])); foreach (['status', 'mode', 'featured', 'previous_start', 'rsvp_enabled', 'rsvp_capacity', 'cost', 'currency', 'website', 'virtual_url', 'show_map'] as $field) { $key = '_pcwp_' . $field; $definition = \ProCalendarWP\Events\EventMeta::definitions()[$key]; $form = 'pcwp_' . $field; if ($definition['type'] === 'boolean') { update_post_meta($id, $key, ! empty($_POST[$form])); } elseif (isset($_POST[$form])) { update_post_meta($id, $key, call_user_func($definition['sanitize_callback'], wp_unslash($_POST[$form]))); } } foreach ([SeriesMeta::EXCLUSIONS, SeriesMeta::ADDITIONS] as $key) { if (isset($_POST[$key])) { update_post_meta($id, $key, SeriesMeta::sanitizeDates(preg_split('/\r?\n/', wp_unslash($_POST[$key])) ?: [])); } } foreach (['_pcwp_venue_ids', '_pcwp_organizer_ids'] as $key) { if (isset($_POST[$key])) { $values = json_decode(wp_unslash($_POST[$key]), true); $type = $key === '_pcwp_venue_ids' ? 'pcwp_venue' : 'pcwp_organizer'; update_post_meta($id, $key, array_values(array_filter(array_map('absint', is_array($values) ? $values : []), static fn (int $entity): bool => get_post_type($entity) === $type))); } } } public function notice(): void { $id = absint($_GET['post'] ?? 0); if (get_post_meta($id, '_pcwp_recurrence_attention', true) && current_user_can('edit_post', $id)) { echo '' . esc_html__('This date was removed from its series and still has registrations. It has been kept as a cancelled event. Review the attendees and schedule before marking it reviewed.', 'proeventswp') . '
' . esc_html($error ?: __('This existing series needs review before switching recurrence engines. Its published dates have been preserved.', 'proeventswp')) . '