'ae', 'ö' => 'oe', 'ü' => 'ue', 'ß' => 'ss', ) ); return preg_replace( '/[^a-z0-9]/', '', $s ); } /** * Map of normalised key → canonical position (1-66), including common * alternative spellings so existing terms are recognised instead of duplicated. * * @return int[] */ function fegp_book_key_map() { static $map = null; if ( null !== $map ) { return $map; } $map = array(); foreach ( fegp_bible_books() as $i => $name ) { $map[ fegp_book_key( $name ) ] = $i + 1; } // Slugs generated without the German locale drop the umlaut ("matthaus"). foreach ( fegp_bible_books() as $i => $name ) { $plain = fegp_book_key( strtr( $name, array( 'ä' => 'a', 'ö' => 'o', 'ü' => 'u' ) ) ); if ( ! isset( $map[ $plain ] ) ) { $map[ $plain ] = $i + 1; } } $aliases = array( 'Psalm' => 'Psalmen', 'Hoheslied' => 'Hohelied', 'Hohelied Salomos' => 'Hohelied', 'Prediger Salomo' => 'Prediger', 'Kohelet' => 'Prediger', 'Klagelieder Jeremias' => 'Klagelieder', 'Ruth' => 'Rut', 'Esther' => 'Ester', 'Ezechiel' => 'Hesekiel', 'Zephanja' => 'Zefanja', 'Apg' => 'Apostelgeschichte', 'Offenbarung des Johannes' => 'Offenbarung', 'Johannes-Offenbarung' => 'Offenbarung', ); $pos = array_flip( fegp_bible_books() ); foreach ( $aliases as $alias => $name ) { $k = fegp_book_key( $alias ); if ( ! isset( $map[ $k ] ) ) { $map[ $k ] = $pos[ $name ] + 1; } } return $map; } /** * Canonical position (1-66) of a term, or 0 if unknown. * * @param WP_Term $term Term. * @return int */ function fegp_book_position( $term ) { $order = (int) get_term_meta( $term->term_id, 'fegp_book_order', true ); if ( $order > 0 ) { return $order; } $map = fegp_book_key_map(); foreach ( array( $term->name, rawurldecode( $term->slug ) ) as $v ) { $k = fegp_book_key( $v ); if ( isset( $map[ $k ] ) ) { return $map[ $k ]; } } return 0; } /** * Sort book terms in biblical order; unknown books follow alphabetically. * * @param WP_Term[] $terms Terms. * @return WP_Term[] */ function fegp_sort_books( $terms ) { if ( ! is_array( $terms ) ) { return $terms; } usort( $terms, function ( $a, $b ) { $pa = fegp_book_position( $a ); $pb = fegp_book_position( $b ); $pa = $pa ? $pa : 1000; $pb = $pb ? $pb : 1000; if ( $pa !== $pb ) { return $pa - $pb; } return strcasecmp( $a->name, $b->name ); } ); return $terms; } /** * Create the missing books and store the canonical order. Existing terms are * matched by normalised name or slug (incl. alternative spellings) and never * renamed or duplicated. Idempotent. * * @return array{created:string[],matched:string[]} */ function fegp_install_books() { $result = array( 'created' => array(), 'matched' => array(), ); if ( ! taxonomy_exists( 'sermon_book' ) ) { return $result; } $existing = get_terms( array( 'taxonomy' => 'sermon_book', 'hide_empty' => false, ) ); if ( is_wp_error( $existing ) ) { return $result; } $map = fegp_book_key_map(); $found = array(); // position => term. foreach ( $existing as $t ) { foreach ( array( $t->name, rawurldecode( $t->slug ) ) as $v ) { $k = fegp_book_key( $v ); if ( isset( $map[ $k ] ) && ! isset( $found[ $map[ $k ] ] ) ) { $found[ $map[ $k ] ] = $t; break; } } } foreach ( fegp_bible_books() as $i => $name ) { $pos = $i + 1; if ( isset( $found[ $pos ] ) ) { $term_id = $found[ $pos ]->term_id; $result['matched'][] = $found[ $pos ]->name; } else { $ins = wp_insert_term( $name, 'sermon_book' ); if ( is_wp_error( $ins ) ) { // A term with this name exists after all: reuse it. $term_id = (int) $ins->get_error_data( 'term_exists' ); if ( ! $term_id ) { continue; } } else { $term_id = (int) $ins['term_id']; $result['created'][] = $name; } } if ( (int) get_term_meta( $term_id, 'fegp_book_order', true ) !== $pos ) { update_term_meta( $term_id, 'fegp_book_order', $pos ); } } return $result; } /** * Run the installation once per book-list version (activation and upgrade). */ function fegp_maybe_install_books() { if ( get_option( 'fegp_books_version' ) === FEGP_BOOKS_VERSION || ! taxonomy_exists( 'sermon_book' ) ) { return; } $res = fegp_install_books(); update_option( 'fegp_books_version', FEGP_BOOKS_VERSION ); update_option( 'fegp_books_install_log', array_merge( $res, array( 'time' => time() ) ), false ); } add_action( 'init', 'fegp_maybe_install_books', 30 ); /** * Replace the tag-style box with an ordered checkbox list. */ function fegp_book_metabox_setup() { if ( ! taxonomy_exists( 'sermon_book' ) ) { return; } remove_meta_box( 'tagsdiv-sermon_book', 'sermons', 'side' ); add_meta_box( 'fegp_books', __( 'Bible books', 'kanzelbuch' ), 'fegp_render_book_metabox', 'sermons', 'side', 'default' ); } add_action( 'add_meta_boxes_sermons', 'fegp_book_metabox_setup' ); /** * Hide the block editor's own panel for books (the ordered metabox replaces it). * * @param WP_REST_Response $response Response. * @param WP_Taxonomy $taxonomy Taxonomy. * @return WP_REST_Response */ function fegp_hide_book_panel( $response, $taxonomy ) { if ( 'sermon_book' === $taxonomy->name ) { $data = $response->get_data(); if ( isset( $data['visibility'] ) ) { $data['visibility']['show_ui'] = false; $response->set_data( $data ); } } return $response; } add_filter( 'rest_prepare_taxonomy', 'fegp_hide_book_panel', 10, 2 ); /** * Markup of the book selector (also used for the preview). * * @param WP_Term[] $terms All book terms, sorted. * @param int[] $selected Selected term IDs. * @return string */ function fegp_book_selector_html( $terms, $selected ) { $groups = array( 'ot' => array( __( 'Old Testament', 'kanzelbuch' ), '' ), 'nt' => array( __( 'New Testament', 'kanzelbuch' ), '' ), 'other' => array( __( 'Other', 'kanzelbuch' ), '' ), ); foreach ( $terms as $t ) { $pos = fegp_book_position( $t ); $g = ! $pos || $pos > 66 ? 'other' : ( $pos <= FEGP_OT_BOOKS ? 'ot' : 'nt' ); $groups[ $g ][1] .= '
  • '; } $html = '
    '; foreach ( $groups as $g ) { if ( '' === $g[1] ) { continue; } $html .= '

    ' . esc_html( $g[0] ) . '

    '; } return $html . '
    '; } /** * Render the book metabox. * * @param WP_Post $post Post. */ function fegp_render_book_metabox( $post ) { wp_nonce_field( 'fegp_save_books', 'fegp_books_nonce' ); $terms = get_terms( array( 'taxonomy' => 'sermon_book', 'hide_empty' => false, ) ); $terms = is_wp_error( $terms ) ? array() : fegp_sort_books( $terms ); $selected = wp_get_object_terms( $post->ID, 'sermon_book', array( 'fields' => 'ids' ) ); $selected = is_wp_error( $selected ) ? array() : array_map( 'intval', $selected ); echo wp_kses( fegp_book_selector_html( $terms, $selected ), fegp_kses_allowed() ); } /** * Save the selected books. * * @param int $post_id Post ID. */ function fegp_save_books( $post_id ) { if ( ! isset( $_POST['fegp_books_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['fegp_books_nonce'] ) ), 'fegp_save_books' ) ) { return; } if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! current_user_can( 'edit_post', $post_id ) ) { return; } $ids = isset( $_POST['fegp_books'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['fegp_books'] ) ) : array(); $ids = array_values( array_filter( $ids ) ); wp_set_object_terms( $post_id, $ids, 'sermon_book', false ); } add_action( 'save_post_sermons', 'fegp_save_books' );