array( 'name' => __( 'Sermons', 'pulpitkit' ), 'singular_name' => __( 'Sermon', 'pulpitkit' ), 'add_new_item' => __( 'Add New Sermon', 'pulpitkit' ), 'edit_item' => __( 'Edit Sermon', 'pulpitkit' ), 'new_item' => __( 'New Sermon', 'pulpitkit' ), 'view_item' => __( 'View Sermon', 'pulpitkit' ), 'search_items' => __( 'Search Sermons', 'pulpitkit' ), 'not_found' => __( 'No sermons found', 'pulpitkit' ), 'not_found_in_trash' => __( 'No sermons found in Trash', 'pulpitkit' ), ), 'public' => true, 'show_in_rest' => true, 'has_archive' => true, 'rewrite' => array( 'slug' => 'sermons' ), 'menu_icon' => 'dashicons-book-alt', 'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'author' ), ) ); register_post_type( 'pulpitkit_event', array( 'labels' => array( 'name' => __( 'Events', 'pulpitkit' ), 'singular_name' => __( 'Event', 'pulpitkit' ), 'add_new_item' => __( 'Add New Event', 'pulpitkit' ), 'edit_item' => __( 'Edit Event', 'pulpitkit' ), 'new_item' => __( 'New Event', 'pulpitkit' ), 'view_item' => __( 'View Event', 'pulpitkit' ), 'search_items' => __( 'Search Events', 'pulpitkit' ), 'not_found' => __( 'No events found', 'pulpitkit' ), 'not_found_in_trash' => __( 'No events found in Trash', 'pulpitkit' ), ), 'public' => true, 'show_in_rest' => true, 'has_archive' => true, 'rewrite' => array( 'slug' => 'events' ), 'menu_icon' => 'dashicons-calendar-alt', 'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail' ), ) ); } /** * Register sermon taxonomies. */ public static function register_taxonomies() { register_taxonomy( 'pulpitkit_sermon_series', array( 'pulpitkit_sermon' ), array( 'labels' => array( 'name' => __( 'Sermon Series', 'pulpitkit' ), 'singular_name' => __( 'Series', 'pulpitkit' ), 'search_items' => __( 'Search Series', 'pulpitkit' ), 'all_items' => __( 'All Series', 'pulpitkit' ), 'edit_item' => __( 'Edit Series', 'pulpitkit' ), 'add_new_item' => __( 'Add New Series', 'pulpitkit' ), ), 'hierarchical' => true, 'public' => true, 'show_in_rest' => true, 'rewrite' => array( 'slug' => 'sermon-series' ), ) ); register_taxonomy( 'pulpitkit_sermon_speaker', array( 'pulpitkit_sermon' ), array( 'labels' => array( 'name' => __( 'Speakers', 'pulpitkit' ), 'singular_name' => __( 'Speaker', 'pulpitkit' ), 'search_items' => __( 'Search Speakers', 'pulpitkit' ), 'all_items' => __( 'All Speakers', 'pulpitkit' ), 'edit_item' => __( 'Edit Speaker', 'pulpitkit' ), 'add_new_item' => __( 'Add New Speaker', 'pulpitkit' ), ), 'hierarchical' => false, 'public' => true, 'show_in_rest' => true, 'rewrite' => array( 'slug' => 'speaker' ), ) ); register_taxonomy( 'pulpitkit_sermon_topic', array( 'pulpitkit_sermon' ), array( 'labels' => array( 'name' => __( 'Topics', 'pulpitkit' ), 'singular_name' => __( 'Topic', 'pulpitkit' ), 'search_items' => __( 'Search Topics', 'pulpitkit' ), 'all_items' => __( 'All Topics', 'pulpitkit' ), 'edit_item' => __( 'Edit Topic', 'pulpitkit' ), 'add_new_item' => __( 'Add New Topic', 'pulpitkit' ), ), 'hierarchical' => false, 'public' => true, 'show_in_rest' => true, 'rewrite' => array( 'slug' => 'sermon-topic' ), ) ); } /** * Constructor: meta boxes + save handlers. */ public function __construct() { add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) ); add_action( 'save_post_pulpitkit_sermon', array( $this, 'save_sermon_meta' ), 10, 2 ); add_action( 'save_post_pulpitkit_event', array( $this, 'save_event_meta' ), 10, 2 ); } /** * Add meta boxes. */ public function add_meta_boxes() { add_meta_box( 'pulpitkit_sermon_details', __( 'Sermon Details', 'pulpitkit' ), array( $this, 'render_sermon_meta_box' ), 'pulpitkit_sermon', 'normal', 'high' ); add_meta_box( 'pulpitkit_event_details', __( 'Event Details', 'pulpitkit' ), array( $this, 'render_event_meta_box' ), 'pulpitkit_event', 'normal', 'high' ); } /** * Render the sermon meta box. * * @param WP_Post $post Current post. */ public function render_sermon_meta_box( $post ) { wp_nonce_field( 'pulpitkit_save_sermon_meta', 'pulpitkit_sermon_meta_nonce' ); $scripture = get_post_meta( $post->ID, '_pulpitkit_scripture', true ); $date = get_post_meta( $post->ID, '_pulpitkit_date_preached', true ); $audio_url = get_post_meta( $post->ID, '_pulpitkit_audio_url', true ); $audio_id = (int) get_post_meta( $post->ID, '_pulpitkit_audio_id', true ); $video_url = get_post_meta( $post->ID, '_pulpitkit_video_url', true ); $notes_url = get_post_meta( $post->ID, '_pulpitkit_notes_url', true ); $notes_id = (int) get_post_meta( $post->ID, '_pulpitkit_notes_id', true ); if ( '' === $date ) { $date = get_the_date( 'Y-m-d', $post ); } ?> can_save( $post_id, 'pulpitkit_sermon_meta_nonce', 'pulpitkit_save_sermon_meta' ) ) { return; } // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce + capability verified in can_save() above; the sniff cannot follow the helper. if ( isset( $_POST['pulpitkit_scripture'] ) ) { update_post_meta( $post_id, '_pulpitkit_scripture', sanitize_text_field( wp_unslash( $_POST['pulpitkit_scripture'] ) ) ); } if ( isset( $_POST['pulpitkit_date_preached'] ) ) { $date = sanitize_text_field( wp_unslash( $_POST['pulpitkit_date_preached'] ) ); update_post_meta( $post_id, '_pulpitkit_date_preached', preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date ) ? $date : '' ); } if ( isset( $_POST['pulpitkit_audio_url'] ) ) { update_post_meta( $post_id, '_pulpitkit_audio_url', esc_url_raw( wp_unslash( $_POST['pulpitkit_audio_url'] ) ) ); } if ( isset( $_POST['pulpitkit_audio_id'] ) ) { update_post_meta( $post_id, '_pulpitkit_audio_id', absint( wp_unslash( $_POST['pulpitkit_audio_id'] ) ) ); } if ( isset( $_POST['pulpitkit_video_url'] ) ) { update_post_meta( $post_id, '_pulpitkit_video_url', esc_url_raw( wp_unslash( $_POST['pulpitkit_video_url'] ) ) ); } if ( isset( $_POST['pulpitkit_notes_url'] ) ) { update_post_meta( $post_id, '_pulpitkit_notes_url', esc_url_raw( wp_unslash( $_POST['pulpitkit_notes_url'] ) ) ); } if ( isset( $_POST['pulpitkit_notes_id'] ) ) { update_post_meta( $post_id, '_pulpitkit_notes_id', absint( wp_unslash( $_POST['pulpitkit_notes_id'] ) ) ); } // phpcs:enable WordPress.Security.NonceVerification.Missing } /** * Render the event meta box. * * @param WP_Post $post Current post. */ public function render_event_meta_box( $post ) { wp_nonce_field( 'pulpitkit_save_event_meta', 'pulpitkit_event_meta_nonce' ); $start = get_post_meta( $post->ID, '_pulpitkit_event_start', true ); $end = get_post_meta( $post->ID, '_pulpitkit_event_end', true ); $location = get_post_meta( $post->ID, '_pulpitkit_location', true ); $recurrence = get_post_meta( $post->ID, '_pulpitkit_recurrence', true ); $recur_end = get_post_meta( $post->ID, '_pulpitkit_recurrence_end', true ); $start_input = $start ? str_replace( ' ', 'T', substr( (string) $start, 0, 16 ) ) : ''; $end_input = $end ? str_replace( ' ', 'T', substr( (string) $end, 0, 16 ) ) : ''; ?> can_save( $post_id, 'pulpitkit_event_meta_nonce', 'pulpitkit_save_event_meta' ) ) { return; } // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce + capability verified in can_save() above; the sniff cannot follow the helper. if ( isset( $_POST['pulpitkit_event_start'] ) ) { update_post_meta( $post_id, '_pulpitkit_event_start', $this->sanitize_datetime( wp_unslash( $_POST['pulpitkit_event_start'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized inside sanitize_datetime(). } if ( isset( $_POST['pulpitkit_event_end'] ) ) { update_post_meta( $post_id, '_pulpitkit_event_end', $this->sanitize_datetime( wp_unslash( $_POST['pulpitkit_event_end'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized inside sanitize_datetime(). } if ( isset( $_POST['pulpitkit_location'] ) ) { update_post_meta( $post_id, '_pulpitkit_location', sanitize_text_field( wp_unslash( $_POST['pulpitkit_location'] ) ) ); } if ( isset( $_POST['pulpitkit_recurrence'] ) ) { $recurrence = sanitize_key( wp_unslash( $_POST['pulpitkit_recurrence'] ) ); if ( ! array_key_exists( $recurrence, PulpitKit_Events::recurrence_options() ) ) { $recurrence = 'none'; } update_post_meta( $post_id, '_pulpitkit_recurrence', $recurrence ); } if ( isset( $_POST['pulpitkit_recurrence_end'] ) ) { $end = sanitize_text_field( wp_unslash( $_POST['pulpitkit_recurrence_end'] ) ); update_post_meta( $post_id, '_pulpitkit_recurrence_end', preg_match( '/^\d{4}-\d{2}-\d{2}$/', $end ) ? $end : '' ); } // phpcs:enable WordPress.Security.NonceVerification.Missing } /** * Nonce + capability + autosave guard shared by save handlers. * * @param int $post_id Post being saved. * @param string $nonce_field POST field name. * @param string $nonce_action Nonce action. * @return bool */ private function can_save( $post_id, $nonce_field, $nonce_action ) { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return false; } if ( ! isset( $_POST[ $nonce_field ] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST[ $nonce_field ] ) ), $nonce_action ) ) { return false; } return current_user_can( 'edit_post', $post_id ); } /** * Sanitize a datetime-local input ("2026-10-13T10:30") to "Y-m-d H:i". * * @param string $raw Raw input. * @return string Sanitized datetime or empty string. */ private function sanitize_datetime( $raw ) { $raw = trim( sanitize_text_field( (string) $raw ) ); if ( '' === $raw ) { return ''; } $raw = str_replace( 'T', ' ', $raw ); $dt = date_create( $raw, wp_timezone() ); if ( false === $dt ) { return ''; } return $dt->format( 'Y-m-d H:i' ); } }