I’ll build this as a complete advanced inc/post-types/glossary.php file for your RX theme: custom post type, taxonomy, meta fields, admin columns, schema, shortcodes, REST support, search helpers, breadcrumbs/permalinks, and safety checks.
Below is an advanced full inc/post-types/glossary.php file for your RX Theme. It creates a complete medical/health glossary system with:
Custom Post Type, glossary categories, tags, A–Z archive support, meta boxes, schema JSON-LD, REST API support, shortcodes, admin columns, search boost, breadcrumbs support, related glossary items, reading time, featured term support, difficulty level, pronunciation, synonyms, references, FAQ-style fields, and SEO-friendly output helpers.
Create this file:
rx-theme/inc/post-types/glossary.php
Then include it in your functions.php:
require_once get_template_directory() . '/inc/post-types/glossary.php';
Now paste this full code inside glossary.php:
<?php
/**
* RX Theme Glossary Post Type
*
* File: inc/post-types/glossary.php
*
* This file creates a powerful glossary system for rxharun.com.
* It is designed for medical, anatomical, diagnostic, disease, lab-test,
* treatment, drug, symptom, and health education terms.
*
* Features:
* - Custom post type: rx_glossary
* - Glossary category taxonomy
* - Glossary tag taxonomy
* - A-Z letter taxonomy
* - Custom meta fields
* - SEO-friendly labels
* - REST API support
* - Admin columns
* - Search integration
* - Shortcodes
* - Schema.org MedicalEntity/DefinedTerm JSON-LD
* - Related glossary helper
* - Reading time helper
* - Breadcrumb helper compatibility
* - Archive title helper
* - Custom excerpt helper
* - Featured glossary support
* - Difficulty level
* - Pronunciation
* - Synonyms
* - References
* - Medical disclaimer support
*
* @package RX_Theme
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'RX_Theme_Glossary' ) ) :
final class RX_Theme_Glossary {
/**
* Post type slug.
*/
const POST_TYPE = 'rx_glossary';
/**
* Main category taxonomy.
*/
const TAX_CATEGORY = 'rx_glossary_category';
/**
* Tag taxonomy.
*/
const TAX_TAG = 'rx_glossary_tag';
/**
* A-Z taxonomy.
*/
const TAX_LETTER = 'rx_glossary_letter';
/**
* Meta nonce.
*/
const NONCE_ACTION = 'rx_glossary_meta_save';
/**
* Meta nonce name.
*/
const NONCE_NAME = 'rx_glossary_meta_nonce';
/**
* Boot class.
*/
public static function init() {
add_action( 'init', array( __CLASS__, 'register_post_type' ), 0 );
add_action( 'init', array( __CLASS__, 'register_taxonomies' ), 0 );
add_action( 'init', array( __CLASS__, 'register_meta_fields' ), 5 );
add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_boxes' ) );
add_action( 'save_post_' . self::POST_TYPE, array( __CLASS__, 'save_meta_boxes' ), 10, 2 );
add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( __CLASS__, 'admin_columns' ) );
add_action( 'manage_' . self::POST_TYPE . '_posts_custom_column', array( __CLASS__, 'admin_columns_content' ), 10, 2 );
add_filter( 'manage_edit-' . self::POST_TYPE . '_sortable_columns', array( __CLASS__, 'sortable_columns' ) );
add_action( 'pre_get_posts', array( __CLASS__, 'admin_column_orderby' ) );
add_filter( 'post_updated_messages', array( __CLASS__, 'post_updated_messages' ) );
add_filter( 'bulk_post_updated_messages', array( __CLASS__, 'bulk_post_updated_messages' ), 10, 2 );
add_filter( 'enter_title_here', array( __CLASS__, 'title_placeholder' ), 10, 2 );
add_filter( 'the_content', array( __CLASS__, 'append_glossary_content' ), 20 );
add_action( 'wp_head', array( __CLASS__, 'output_schema_json_ld' ), 30 );
add_shortcode( 'rx_glossary_index', array( __CLASS__, 'shortcode_glossary_index' ) );
add_shortcode( 'rx_glossary_search', array( __CLASS__, 'shortcode_glossary_search' ) );
add_shortcode( 'rx_glossary_related', array( __CLASS__, 'shortcode_related_glossary' ) );
add_shortcode( 'rx_glossary_featured', array( __CLASS__, 'shortcode_featured_glossary' ) );
add_filter( 'template_include', array( __CLASS__, 'template_loader' ) );
add_filter( 'body_class', array( __CLASS__, 'body_classes' ) );
add_filter( 'wp_sitemaps_post_types', array( __CLASS__, 'enable_sitemap' ) );
add_action( 'restrict_manage_posts', array( __CLASS__, 'admin_filter_dropdowns' ) );
add_filter( 'parse_query', array( __CLASS__, 'admin_filter_query' ) );
add_action( 'wp_ajax_rx_glossary_live_search', array( __CLASS__, 'ajax_live_search' ) );
add_action( 'wp_ajax_nopriv_rx_glossary_live_search', array( __CLASS__, 'ajax_live_search' ) );
add_filter( 'rest_prepare_' . self::POST_TYPE, array( __CLASS__, 'rest_prepare_glossary' ), 10, 3 );
add_action( 'transition_post_status', array( __CLASS__, 'auto_assign_first_letter' ), 10, 3 );
add_action( 'save_post_' . self::POST_TYPE, array( __CLASS__, 'auto_assign_first_letter_on_save' ), 20, 3 );
add_filter( 'posts_search', array( __CLASS__, 'improve_glossary_search' ), 10, 2 );
}
/**
* Register glossary custom post type.
*/
public static function register_post_type() {
$labels = array(
'name' => _x( 'Glossary', 'Post type general name', 'rx-theme' ),
'singular_name' => _x( 'Glossary Term', 'Post type singular name', 'rx-theme' ),
'menu_name' => __( 'Glossary', 'rx-theme' ),
'name_admin_bar' => __( 'Glossary Term', 'rx-theme' ),
'add_new' => __( 'Add New', 'rx-theme' ),
'add_new_item' => __( 'Add New Glossary Term', 'rx-theme' ),
'new_item' => __( 'New Glossary Term', 'rx-theme' ),
'edit_item' => __( 'Edit Glossary Term', 'rx-theme' ),
'view_item' => __( 'View Glossary Term', 'rx-theme' ),
'all_items' => __( 'All Glossary Terms', 'rx-theme' ),
'search_items' => __( 'Search Glossary Terms', 'rx-theme' ),
'parent_item_colon' => __( 'Parent Glossary Terms:', 'rx-theme' ),
'not_found' => __( 'No glossary terms found.', 'rx-theme' ),
'not_found_in_trash' => __( 'No glossary terms found in Trash.', 'rx-theme' ),
'featured_image' => __( 'Glossary Image', 'rx-theme' ),
'set_featured_image' => __( 'Set glossary image', 'rx-theme' ),
'remove_featured_image' => __( 'Remove glossary image', 'rx-theme' ),
'use_featured_image' => __( 'Use as glossary image', 'rx-theme' ),
'archives' => __( 'Glossary Archives', 'rx-theme' ),
'insert_into_item' => __( 'Insert into glossary term', 'rx-theme' ),
'uploaded_to_this_item' => __( 'Uploaded to this glossary term', 'rx-theme' ),
'filter_items_list' => __( 'Filter glossary list', 'rx-theme' ),
'items_list_navigation' => __( 'Glossary list navigation', 'rx-theme' ),
'items_list' => __( 'Glossary list', 'rx-theme' ),
);
$args = array(
'labels' => $labels,
'description' => __( 'Medical and health glossary terms for RX Theme.', 'rx-theme' ),
'public' => true,
'publicly_queryable' => true,
'exclude_from_search' => false,
'show_ui' => true,
'show_in_menu' => true,
'show_in_nav_menus' => true,
'show_in_admin_bar' => true,
'show_in_rest' => true,
'rest_base' => 'glossary',
'rest_controller_class' => 'WP_REST_Posts_Controller',
'menu_position' => 21,
'menu_icon' => 'dashicons-book-alt',
'capability_type' => 'post',
'map_meta_cap' => true,
'hierarchical' => false,
'supports' => array(
'title',
'editor',
'excerpt',
'author',
'thumbnail',
'comments',
'revisions',
'custom-fields',
'page-attributes',
),
'taxonomies' => array(
self::TAX_CATEGORY,
self::TAX_TAG,
self::TAX_LETTER,
),
'has_archive' => 'glossary',
'rewrite' => array(
'slug' => 'glossary',
'with_front' => false,
'feeds' => true,
'pages' => true,
),
'query_var' => true,
'can_export' => true,
'delete_with_user' => false,
);
register_post_type( self::POST_TYPE, $args );
}
/**
* Register taxonomies.
*/
public static function register_taxonomies() {
$category_labels = array(
'name' => _x( 'Glossary Categories', 'taxonomy general name', 'rx-theme' ),
'singular_name' => _x( 'Glossary Category', 'taxonomy singular name', 'rx-theme' ),
'search_items' => __( 'Search Glossary Categories', 'rx-theme' ),
'all_items' => __( 'All Glossary Categories', 'rx-theme' ),
'parent_item' => __( 'Parent Glossary Category', 'rx-theme' ),
'parent_item_colon' => __( 'Parent Glossary Category:', 'rx-theme' ),
'edit_item' => __( 'Edit Glossary Category', 'rx-theme' ),
'update_item' => __( 'Update Glossary Category', 'rx-theme' ),
'add_new_item' => __( 'Add New Glossary Category', 'rx-theme' ),
'new_item_name' => __( 'New Glossary Category Name', 'rx-theme' ),
'menu_name' => __( 'Glossary Categories', 'rx-theme' ),
);
register_taxonomy(
self::TAX_CATEGORY,
array( self::POST_TYPE ),
array(
'hierarchical' => true,
'labels' => $category_labels,
'show_ui' => true,
'show_admin_column' => true,
'show_in_nav_menus' => true,
'show_tagcloud' => true,
'show_in_rest' => true,
'rest_base' => 'glossary-categories',
'query_var' => true,
'rewrite' => array(
'slug' => 'glossary-category',
'with_front' => false,
'hierarchical' => true,
),
)
);
$tag_labels = array(
'name' => _x( 'Glossary Tags', 'taxonomy general name', 'rx-theme' ),
'singular_name' => _x( 'Glossary Tag', 'taxonomy singular name', 'rx-theme' ),
'search_items' => __( 'Search Glossary Tags', 'rx-theme' ),
'popular_items' => __( 'Popular Glossary Tags', 'rx-theme' ),
'all_items' => __( 'All Glossary Tags', 'rx-theme' ),
'edit_item' => __( 'Edit Glossary Tag', 'rx-theme' ),
'update_item' => __( 'Update Glossary Tag', 'rx-theme' ),
'add_new_item' => __( 'Add New Glossary Tag', 'rx-theme' ),
'new_item_name' => __( 'New Glossary Tag Name', 'rx-theme' ),
'separate_items_with_commas' => __( 'Separate tags with commas', 'rx-theme' ),
'add_or_remove_items' => __( 'Add or remove tags', 'rx-theme' ),
'choose_from_most_used' => __( 'Choose from the most used tags', 'rx-theme' ),
'not_found' => __( 'No glossary tags found.', 'rx-theme' ),
'menu_name' => __( 'Glossary Tags', 'rx-theme' ),
);
register_taxonomy(
self::TAX_TAG,
array( self::POST_TYPE ),
array(
'hierarchical' => false,
'labels' => $tag_labels,
'show_ui' => true,
'show_admin_column' => true,
'show_in_nav_menus' => true,
'show_tagcloud' => true,
'show_in_rest' => true,
'rest_base' => 'glossary-tags',
'query_var' => true,
'update_count_callback' => '_update_post_term_count',
'rewrite' => array(
'slug' => 'glossary-tag',
'with_front' => false,
),
)
);
$letter_labels = array(
'name' => _x( 'Glossary Letters', 'taxonomy general name', 'rx-theme' ),
'singular_name' => _x( 'Glossary Letter', 'taxonomy singular name', 'rx-theme' ),
'search_items' => __( 'Search Letters', 'rx-theme' ),
'all_items' => __( 'All Letters', 'rx-theme' ),
'edit_item' => __( 'Edit Letter', 'rx-theme' ),
'update_item' => __( 'Update Letter', 'rx-theme' ),
'add_new_item' => __( 'Add New Letter', 'rx-theme' ),
'new_item_name' => __( 'New Letter Name', 'rx-theme' ),
'menu_name' => __( 'A-Z Letters', 'rx-theme' ),
);
register_taxonomy(
self::TAX_LETTER,
array( self::POST_TYPE ),
array(
'hierarchical' => false,
'labels' => $letter_labels,
'show_ui' => true,
'show_admin_column' => true,
'show_in_nav_menus' => true,
'show_tagcloud' => false,
'show_in_rest' => true,
'rest_base' => 'glossary-letters',
'query_var' => true,
'rewrite' => array(
'slug' => 'glossary-letter',
'with_front' => false,
),
)
);
self::maybe_create_default_letters();
}
/**
* Create A-Z terms if missing.
*/
public static function maybe_create_default_letters() {
$letters = range( 'A', 'Z' );
foreach ( $letters as $letter ) {
if ( ! term_exists( $letter, self::TAX_LETTER ) ) {
wp_insert_term(
$letter,
self::TAX_LETTER,
array(
'slug' => strtolower( $letter ),
)
);
}
}
if ( ! term_exists( '0-9', self::TAX_LETTER ) ) {
wp_insert_term(
'0-9',
self::TAX_LETTER,
array(
'slug' => '0-9',
)
);
}
}
/**
* Register meta fields for REST and sanitization.
*/
public static function register_meta_fields() {
$fields = self::get_meta_fields();
foreach ( $fields as $key => $field ) {
register_post_meta(
self::POST_TYPE,
$key,
array(
'type' => isset( $field['type'] ) ? $field['type'] : 'string',
'single' => true,
'sanitize_callback' => isset( $field['sanitize'] ) ? $field['sanitize'] : 'sanitize_text_field',
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
},
'show_in_rest' => isset( $field['rest'] ) ? (bool) $field['rest'] : true,
)
);
}
}
/**
* Meta fields list.
*/
public static function get_meta_fields() {
return array(
'_rx_glossary_short_definition' => array(
'label' => __( 'Short Definition', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_textarea_field',
'rest' => true,
),
'_rx_glossary_long_definition' => array(
'label' => __( 'Long Definition', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'wp_kses_post',
'rest' => true,
),
'_rx_glossary_pronunciation' => array(
'label' => __( 'Pronunciation', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_text_field',
'rest' => true,
),
'_rx_glossary_audio_url' => array(
'label' => __( 'Pronunciation Audio URL', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'esc_url_raw',
'rest' => true,
),
'_rx_glossary_synonyms' => array(
'label' => __( 'Synonyms / Other Names', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_textarea_field',
'rest' => true,
),
'_rx_glossary_related_terms' => array(
'label' => __( 'Related Terms', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_textarea_field',
'rest' => true,
),
'_rx_glossary_medical_specialty' => array(
'label' => __( 'Medical Specialty', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_text_field',
'rest' => true,
),
'_rx_glossary_body_system' => array(
'label' => __( 'Body System', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_text_field',
'rest' => true,
),
'_rx_glossary_difficulty' => array(
'label' => __( 'Difficulty Level', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_text_field',
'rest' => true,
),
'_rx_glossary_featured' => array(
'label' => __( 'Featured Glossary Term', 'rx-theme' ),
'type' => 'boolean',
'sanitize' => array( __CLASS__, 'sanitize_checkbox' ),
'rest' => true,
),
'_rx_glossary_reviewed_by' => array(
'label' => __( 'Reviewed By', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_text_field',
'rest' => true,
),
'_rx_glossary_reviewed_date' => array(
'label' => __( 'Reviewed Date', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_text_field',
'rest' => true,
),
'_rx_glossary_source_references' => array(
'label' => __( 'References / Sources', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'wp_kses_post',
'rest' => true,
),
'_rx_glossary_faq_question_1' => array(
'label' => __( 'FAQ Question 1', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_text_field',
'rest' => true,
),
'_rx_glossary_faq_answer_1' => array(
'label' => __( 'FAQ Answer 1', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'wp_kses_post',
'rest' => true,
),
'_rx_glossary_faq_question_2' => array(
'label' => __( 'FAQ Question 2', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_text_field',
'rest' => true,
),
'_rx_glossary_faq_answer_2' => array(
'label' => __( 'FAQ Answer 2', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'wp_kses_post',
'rest' => true,
),
'_rx_glossary_faq_question_3' => array(
'label' => __( 'FAQ Question 3', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'sanitize_text_field',
'rest' => true,
),
'_rx_glossary_faq_answer_3' => array(
'label' => __( 'FAQ Answer 3', 'rx-theme' ),
'type' => 'string',
'sanitize' => 'wp_kses_post',
'rest' => true,
),
);
}
/**
* Add meta boxes.
*/
public static function add_meta_boxes() {
add_meta_box(
'rx_glossary_core_meta',
__( 'Glossary Details', 'rx-theme' ),
array( __CLASS__, 'render_core_meta_box' ),
self::POST_TYPE,
'normal',
'high'
);
add_meta_box(
'rx_glossary_medical_meta',
__( 'Medical Classification', 'rx-theme' ),
array( __CLASS__, 'render_medical_meta_box' ),
self::POST_TYPE,
'side',
'default'
);
add_meta_box(
'rx_glossary_review_meta',
__( 'Review & References', 'rx-theme' ),
array( __CLASS__, 'render_review_meta_box' ),
self::POST_TYPE,
'normal',
'default'
);
add_meta_box(
'rx_glossary_faq_meta',
__( 'Glossary FAQs', 'rx-theme' ),
array( __CLASS__, 'render_faq_meta_box' ),
self::POST_TYPE,
'normal',
'default'
);
}
/**
* Render core meta box.
*/
public static function render_core_meta_box( $post ) {
wp_nonce_field( self::NONCE_ACTION, self::NONCE_NAME );
$short_definition = get_post_meta( $post->ID, '_rx_glossary_short_definition', true );
$long_definition = get_post_meta( $post->ID, '_rx_glossary_long_definition', true );
$pronunciation = get_post_meta( $post->ID, '_rx_glossary_pronunciation', true );
$audio_url = get_post_meta( $post->ID, '_rx_glossary_audio_url', true );
$synonyms = get_post_meta( $post->ID, '_rx_glossary_synonyms', true );
$related_terms = get_post_meta( $post->ID, '_rx_glossary_related_terms', true );
?>
<p>
<label for="rx_glossary_short_definition"><strong><?php esc_html_e( 'Short Definition', 'rx-theme' ); ?></strong></label>
<textarea id="rx_glossary_short_definition" name="rx_glossary_short_definition" rows="3" class="large-text"><?php echo esc_textarea( $short_definition ); ?></textarea>
<span class="description"><?php esc_html_e( 'A simple 1–2 sentence definition for users and search snippets.', 'rx-theme' ); ?></span>
</p>
<p>
<label for="rx_glossary_long_definition"><strong><?php esc_html_e( 'Long Definition', 'rx-theme' ); ?></strong></label>
<?php
wp_editor(
$long_definition,
'rx_glossary_long_definition',
array(
'textarea_name' => 'rx_glossary_long_definition',
'textarea_rows' => 8,
'media_buttons' => false,
'teeny' => false,
)
);
?>
</p>
<p>
<label for="rx_glossary_pronunciation"><strong><?php esc_html_e( 'Pronunciation', 'rx-theme' ); ?></strong></label>
<input type="text" id="rx_glossary_pronunciation" name="rx_glossary_pronunciation" value="<?php echo esc_attr( $pronunciation ); ?>" class="large-text" placeholder="Example: new-TROH-fee-nee-ah">
</p>
<p>
<label for="rx_glossary_audio_url"><strong><?php esc_html_e( 'Audio URL', 'rx-theme' ); ?></strong></label>
<input type="url" id="rx_glossary_audio_url" name="rx_glossary_audio_url" value="<?php echo esc_url( $audio_url ); ?>" class="large-text" placeholder="https://example.com/audio.mp3">
</p>
<p>
<label for="rx_glossary_synonyms"><strong><?php esc_html_e( 'Synonyms / Other Names', 'rx-theme' ); ?></strong></label>
<textarea id="rx_glossary_synonyms" name="rx_glossary_synonyms" rows="3" class="large-text" placeholder="One or more names, separated by commas"><?php echo esc_textarea( $synonyms ); ?></textarea>
</p>
<p>
<label for="rx_glossary_related_terms"><strong><?php esc_html_e( 'Related Terms', 'rx-theme' ); ?></strong></label>
<textarea id="rx_glossary_related_terms" name="rx_glossary_related_terms" rows="3" class="large-text" placeholder="Example: CBC, Neutrophils, Hematocrit"><?php echo esc_textarea( $related_terms ); ?></textarea>
</p>
<?php
}
/**
* Render medical meta box.
*/
public static function render_medical_meta_box( $post ) {
$specialty = get_post_meta( $post->ID, '_rx_glossary_medical_specialty', true );
$body = get_post_meta( $post->ID, '_rx_glossary_body_system', true );
$difficulty = get_post_meta( $post->ID, '_rx_glossary_difficulty', true );
$featured = get_post_meta( $post->ID, '_rx_glossary_featured', true );
?>
<p>
<label for="rx_glossary_medical_specialty"><strong><?php esc_html_e( 'Medical Specialty', 'rx-theme' ); ?></strong></label>
<input type="text" id="rx_glossary_medical_specialty" name="rx_glossary_medical_specialty" value="<?php echo esc_attr( $specialty ); ?>" class="widefat" placeholder="Example: Hematology">
</p>
<p>
<label for="rx_glossary_body_system"><strong><?php esc_html_e( 'Body System', 'rx-theme' ); ?></strong></label>
<input type="text" id="rx_glossary_body_system" name="rx_glossary_body_system" value="<?php echo esc_attr( $body ); ?>" class="widefat" placeholder="Example: Blood system">
</p>
<p>
<label for="rx_glossary_difficulty"><strong><?php esc_html_e( 'Difficulty Level', 'rx-theme' ); ?></strong></label>
<select id="rx_glossary_difficulty" name="rx_glossary_difficulty" class="widefat">
<option value=""><?php esc_html_e( 'Select difficulty', 'rx-theme' ); ?></option>
<option value="beginner" <?php selected( $difficulty, 'beginner' ); ?>><?php esc_html_e( 'Beginner', 'rx-theme' ); ?></option>
<option value="intermediate" <?php selected( $difficulty, 'intermediate' ); ?>><?php esc_html_e( 'Intermediate', 'rx-theme' ); ?></option>
<option value="advanced" <?php selected( $difficulty, 'advanced' ); ?>><?php esc_html_e( 'Advanced', 'rx-theme' ); ?></option>
<option value="professional" <?php selected( $difficulty, 'professional' ); ?>><?php esc_html_e( 'Professional', 'rx-theme' ); ?></option>
</select>
</p>
<p>
<label>
<input type="checkbox" name="rx_glossary_featured" value="1" <?php checked( $featured, '1' ); ?>>
<?php esc_html_e( 'Mark as featured glossary term', 'rx-theme' ); ?>
</label>
</p>
<?php
}
/**
* Render review meta box.
*/
public static function render_review_meta_box( $post ) {
$reviewed_by = get_post_meta( $post->ID, '_rx_glossary_reviewed_by', true );
$review_date = get_post_meta( $post->ID, '_rx_glossary_reviewed_date', true );
$references = get_post_meta( $post->ID, '_rx_glossary_source_references', true );
?>
<p>
<label for="rx_glossary_reviewed_by"><strong><?php esc_html_e( 'Reviewed By', 'rx-theme' ); ?></strong></label>
<input type="text" id="rx_glossary_reviewed_by" name="rx_glossary_reviewed_by" value="<?php echo esc_attr( $reviewed_by ); ?>" class="large-text" placeholder="Example: Rx Harun Medical Review Board">
</p>
<p>
<label for="rx_glossary_reviewed_date"><strong><?php esc_html_e( 'Reviewed Date', 'rx-theme' ); ?></strong></label>
<input type="date" id="rx_glossary_reviewed_date" name="rx_glossary_reviewed_date" value="<?php echo esc_attr( $review_date ); ?>" class="regular-text">
</p>
<p>
<label for="rx_glossary_source_references"><strong><?php esc_html_e( 'References / Sources', 'rx-theme' ); ?></strong></label>
<?php
wp_editor(
$references,
'rx_glossary_source_references',
array(
'textarea_name' => 'rx_glossary_source_references',
'textarea_rows' => 7,
'media_buttons' => false,
'teeny' => false,
)
);
?>
</p>
<?php
}
/**
* Render FAQ meta box.
*/
public static function render_faq_meta_box( $post ) {
for ( $i = 1; $i <= 3; $i++ ) {
$question = get_post_meta( $post->ID, '_rx_glossary_faq_question_' . $i, true );
$answer = get_post_meta( $post->ID, '_rx_glossary_faq_answer_' . $i, true );
?>
<div style="padding:12px 0;border-bottom:1px solid #ddd;">
<p>
<label for="rx_glossary_faq_question_<?php echo esc_attr( $i ); ?>">
<strong><?php echo esc_html( sprintf( __( 'FAQ Question %d', 'rx-theme' ), $i ) ); ?></strong>
</label>
<input type="text" id="rx_glossary_faq_question_<?php echo esc_attr( $i ); ?>" name="rx_glossary_faq_question_<?php echo esc_attr( $i ); ?>" value="<?php echo esc_attr( $question ); ?>" class="large-text">
</p>
<p>
<label for="rx_glossary_faq_answer_<?php echo esc_attr( $i ); ?>">
<strong><?php echo esc_html( sprintf( __( 'FAQ Answer %d', 'rx-theme' ), $i ) ); ?></strong>
</label>
<textarea id="rx_glossary_faq_answer_<?php echo esc_attr( $i ); ?>" name="rx_glossary_faq_answer_<?php echo esc_attr( $i ); ?>" rows="4" class="large-text"><?php echo esc_textarea( $answer ); ?></textarea>
</p>
</div>
<?php
}
}
/**
* Save meta boxes.
*/
public static function save_meta_boxes( $post_id, $post ) {
if ( ! isset( $_POST[ self::NONCE_NAME ] ) ) {
return;
}
if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ self::NONCE_NAME ] ) ), self::NONCE_ACTION ) ) {
return;
}
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( wp_is_post_revision( $post_id ) ) {
return;
}
if ( self::POST_TYPE !== $post->post_type ) {
return;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
$map = array(
'_rx_glossary_short_definition' => array(
'field' => 'rx_glossary_short_definition',
'sanitize' => 'sanitize_textarea_field',
),
'_rx_glossary_long_definition' => array(
'field' => 'rx_glossary_long_definition',
'sanitize' => 'wp_kses_post',
),
'_rx_glossary_pronunciation' => array(
'field' => 'rx_glossary_pronunciation',
'sanitize' => 'sanitize_text_field',
),
'_rx_glossary_audio_url' => array(
'field' => 'rx_glossary_audio_url',
'sanitize' => 'esc_url_raw',
),
'_rx_glossary_synonyms' => array(
'field' => 'rx_glossary_synonyms',
'sanitize' => 'sanitize_textarea_field',
),
'_rx_glossary_related_terms' => array(
'field' => 'rx_glossary_related_terms',
'sanitize' => 'sanitize_textarea_field',
),
'_rx_glossary_medical_specialty' => array(
'field' => 'rx_glossary_medical_specialty',
'sanitize' => 'sanitize_text_field',
),
'_rx_glossary_body_system' => array(
'field' => 'rx_glossary_body_system',
'sanitize' => 'sanitize_text_field',
),
'_rx_glossary_difficulty' => array(
'field' => 'rx_glossary_difficulty',
'sanitize' => 'sanitize_text_field',
),
'_rx_glossary_reviewed_by' => array(
'field' => 'rx_glossary_reviewed_by',
'sanitize' => 'sanitize_text_field',
),
'_rx_glossary_reviewed_date' => array(
'field' => 'rx_glossary_reviewed_date',
'sanitize' => 'sanitize_text_field',
),
'_rx_glossary_source_references' => array(
'field' => 'rx_glossary_source_references',
'sanitize' => 'wp_kses_post',
),
);
for ( $i = 1; $i <= 3; $i++ ) {
$map[ '_rx_glossary_faq_question_' . $i ] = array(
'field' => 'rx_glossary_faq_question_' . $i,
'sanitize' => 'sanitize_text_field',
);
$map[ '_rx_glossary_faq_answer_' . $i ] = array(
'field' => 'rx_glossary_faq_answer_' . $i,
'sanitize' => 'wp_kses_post',
);
}
foreach ( $map as $meta_key => $data ) {
if ( isset( $_POST[ $data['field'] ] ) ) {
$value = wp_unslash( $_POST[ $data['field'] ] );
$value = call_user_func( $data['sanitize'], $value );
update_post_meta( $post_id, $meta_key, $value );
} else {
delete_post_meta( $post_id, $meta_key );
}
}
$featured = isset( $_POST['rx_glossary_featured'] ) ? '1' : '0';
update_post_meta( $post_id, '_rx_glossary_featured', $featured );
}
/**
* Sanitize checkbox.
*/
public static function sanitize_checkbox( $value ) {
return ! empty( $value ) ? '1' : '0';
}
/**
* Admin columns.
*/
public static function admin_columns( $columns ) {
$new = array();
$new['cb'] = isset( $columns['cb'] ) ? $columns['cb'] : '';
$new['title'] = __( 'Glossary Term', 'rx-theme' );
$new['rx_letter'] = __( 'Letter', 'rx-theme' );
$new['rx_category'] = __( 'Category', 'rx-theme' );
$new['rx_specialty'] = __( 'Specialty', 'rx-theme' );
$new['rx_difficulty'] = __( 'Difficulty', 'rx-theme' );
$new['rx_featured'] = __( 'Featured', 'rx-theme' );
$new['rx_reviewed'] = __( 'Reviewed', 'rx-theme' );
$new['date'] = isset( $columns['date'] ) ? $columns['date'] : __( 'Date', 'rx-theme' );
return $new;
}
/**
* Admin column content.
*/
public static function admin_columns_content( $column, $post_id ) {
switch ( $column ) {
case 'rx_letter':
echo wp_kses_post( get_the_term_list( $post_id, self::TAX_LETTER, '', ', ', '' ) );
break;
case 'rx_category':
echo wp_kses_post( get_the_term_list( $post_id, self::TAX_CATEGORY, '', ', ', '' ) );
break;
case 'rx_specialty':
echo esc_html( get_post_meta( $post_id, '_rx_glossary_medical_specialty', true ) );
break;
case 'rx_difficulty':
$difficulty = get_post_meta( $post_id, '_rx_glossary_difficulty', true );
echo $difficulty ? esc_html( ucfirst( $difficulty ) ) : '—';
break;
case 'rx_featured':
$featured = get_post_meta( $post_id, '_rx_glossary_featured', true );
echo '1' === $featured ? '<span style="color:#008000;font-weight:700;">Yes</span>' : '—';
break;
case 'rx_reviewed':
$date = get_post_meta( $post_id, '_rx_glossary_reviewed_date', true );
echo $date ? esc_html( $date ) : '—';
break;
}
}
/**
* Sortable columns.
*/
public static function sortable_columns( $columns ) {
$columns['rx_specialty'] = 'rx_specialty';
$columns['rx_difficulty'] = 'rx_difficulty';
$columns['rx_featured'] = 'rx_featured';
$columns['rx_reviewed'] = 'rx_reviewed';
return $columns;
}
/**
* Admin order by meta.
*/
public static function admin_column_orderby( $query ) {
if ( ! is_admin() || ! $query->is_main_query() ) {
return;
}
$orderby = $query->get( 'orderby' );
$map = array(
'rx_specialty' => '_rx_glossary_medical_specialty',
'rx_difficulty' => '_rx_glossary_difficulty',
'rx_featured' => '_rx_glossary_featured',
'rx_reviewed' => '_rx_glossary_reviewed_date',
);
if ( isset( $map[ $orderby ] ) ) {
$query->set( 'meta_key', $map[ $orderby ] );
$query->set( 'orderby', 'meta_value' );
}
}
/**
* Admin filters.
*/
public static function admin_filter_dropdowns() {
global $typenow;
if ( self::POST_TYPE !== $typenow ) {
return;
}
self::taxonomy_filter_dropdown( self::TAX_CATEGORY, __( 'All Glossary Categories', 'rx-theme' ) );
self::taxonomy_filter_dropdown( self::TAX_LETTER, __( 'All Letters', 'rx-theme' ) );
$difficulty = isset( $_GET['rx_glossary_difficulty_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['rx_glossary_difficulty_filter'] ) ) : '';
?>
<select name="rx_glossary_difficulty_filter">
<option value=""><?php esc_html_e( 'All Difficulties', 'rx-theme' ); ?></option>
<option value="beginner" <?php selected( $difficulty, 'beginner' ); ?>><?php esc_html_e( 'Beginner', 'rx-theme' ); ?></option>
<option value="intermediate" <?php selected( $difficulty, 'intermediate' ); ?>><?php esc_html_e( 'Intermediate', 'rx-theme' ); ?></option>
<option value="advanced" <?php selected( $difficulty, 'advanced' ); ?>><?php esc_html_e( 'Advanced', 'rx-theme' ); ?></option>
<option value="professional" <?php selected( $difficulty, 'professional' ); ?>><?php esc_html_e( 'Professional', 'rx-theme' ); ?></option>
</select>
<?php
}
/**
* Taxonomy filter dropdown helper.
*/
public static function taxonomy_filter_dropdown( $taxonomy, $label ) {
$selected = isset( $_GET[ $taxonomy ] ) ? sanitize_text_field( wp_unslash( $_GET[ $taxonomy ] ) ) : '';
wp_dropdown_categories(
array(
'show_option_all' => $label,
'taxonomy' => $taxonomy,
'name' => $taxonomy,
'orderby' => 'name',
'selected' => $selected,
'hierarchical' => true,
'depth' => 3,
'show_count' => true,
'hide_empty' => false,
'value_field' => 'slug',
)
);
}
/**
* Admin filter query.
*/
public static function admin_filter_query( $query ) {
global $pagenow;
if ( ! is_admin() || 'edit.php' !== $pagenow ) {
return;
}
$post_type = isset( $_GET['post_type'] ) ? sanitize_key( $_GET['post_type'] ) : '';
if ( self::POST_TYPE !== $post_type ) {
return;
}
$difficulty = isset( $_GET['rx_glossary_difficulty_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['rx_glossary_difficulty_filter'] ) ) : '';
if ( $difficulty ) {
$query->query_vars['meta_key'] = '_rx_glossary_difficulty';
$query->query_vars['meta_value'] = $difficulty;
}
}
/**
* Update messages.
*/
public static function post_updated_messages( $messages ) {
$messages[ self::POST_TYPE ] = array(
0 => '',
1 => __( 'Glossary term updated.', 'rx-theme' ),
2 => __( 'Custom field updated.', 'rx-theme' ),
3 => __( 'Custom field deleted.', 'rx-theme' ),
4 => __( 'Glossary term updated.', 'rx-theme' ),
5 => __( 'Glossary term restored to revision.', 'rx-theme' ),
6 => __( 'Glossary term published.', 'rx-theme' ),
7 => __( 'Glossary term saved.', 'rx-theme' ),
8 => __( 'Glossary term submitted.', 'rx-theme' ),
9 => __( 'Glossary term scheduled.', 'rx-theme' ),
10 => __( 'Glossary term draft updated.', 'rx-theme' ),
);
return $messages;
}
/**
* Bulk updated messages.
*/
public static function bulk_post_updated_messages( $bulk_messages, $bulk_counts ) {
$bulk_messages[ self::POST_TYPE ] = array(
'updated' => _n( '%s glossary term updated.', '%s glossary terms updated.', $bulk_counts['updated'], 'rx-theme' ),
'locked' => _n( '%s glossary term not updated, somebody is editing it.', '%s glossary terms not updated, somebody is editing them.', $bulk_counts['locked'], 'rx-theme' ),
'deleted' => _n( '%s glossary term permanently deleted.', '%s glossary terms permanently deleted.', $bulk_counts['deleted'], 'rx-theme' ),
'trashed' => _n( '%s glossary term moved to the Trash.', '%s glossary terms moved to the Trash.', $bulk_counts['trashed'], 'rx-theme' ),
'untrashed' => _n( '%s glossary term restored from the Trash.', '%s glossary terms restored from the Trash.', $bulk_counts['untrashed'], 'rx-theme' ),
);
return $bulk_messages;
}
/**
* Title placeholder.
*/
public static function title_placeholder( $title, $post ) {
if ( self::POST_TYPE === $post->post_type ) {
return __( 'Enter glossary term name, example: Neutropenia', 'rx-theme' );
}
return $title;
}
/**
* Append glossary content sections.
*/
public static function append_glossary_content( $content ) {
if ( ! is_singular( self::POST_TYPE ) || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
$post_id = get_the_ID();
$short_definition = get_post_meta( $post_id, '_rx_glossary_short_definition', true );
$long_definition = get_post_meta( $post_id, '_rx_glossary_long_definition', true );
$pronunciation = get_post_meta( $post_id, '_rx_glossary_pronunciation', true );
$audio_url = get_post_meta( $post_id, '_rx_glossary_audio_url', true );
$synonyms = get_post_meta( $post_id, '_rx_glossary_synonyms', true );
$related_terms = get_post_meta( $post_id, '_rx_glossary_related_terms', true );
$specialty = get_post_meta( $post_id, '_rx_glossary_medical_specialty', true );
$body_system = get_post_meta( $post_id, '_rx_glossary_body_system', true );
$difficulty = get_post_meta( $post_id, '_rx_glossary_difficulty', true );
$reviewed_by = get_post_meta( $post_id, '_rx_glossary_reviewed_by', true );
$reviewed_date = get_post_meta( $post_id, '_rx_glossary_reviewed_date', true );
$references = get_post_meta( $post_id, '_rx_glossary_source_references', true );
ob_start();
?>
<div class="rx-glossary-box rx-glossary-summary">
<?php if ( $short_definition ) : ?>
<div class="rx-glossary-short-definition">
<h2><?php esc_html_e( 'Simple Definition', 'rx-theme' ); ?></h2>
<p><?php echo esc_html( $short_definition ); ?></p>
</div>
<?php endif; ?>
<?php if ( $pronunciation || $audio_url ) : ?>
<div class="rx-glossary-pronunciation">
<h2><?php esc_html_e( 'Pronunciation', 'rx-theme' ); ?></h2>
<?php if ( $pronunciation ) : ?>
<p><strong><?php esc_html_e( 'How to say it:', 'rx-theme' ); ?></strong> <?php echo esc_html( $pronunciation ); ?></p>
<?php endif; ?>
<?php if ( $audio_url ) : ?>
<audio controls preload="none">
<source src="<?php echo esc_url( $audio_url ); ?>">
<?php esc_html_e( 'Your browser does not support the audio element.', 'rx-theme' ); ?>
</audio>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ( $synonyms ) : ?>
<div class="rx-glossary-synonyms">
<h2><?php esc_html_e( 'Other Names', 'rx-theme' ); ?></h2>
<p><?php echo esc_html( $synonyms ); ?></p>
</div>
<?php endif; ?>
<?php if ( $long_definition ) : ?>
<div class="rx-glossary-long-definition">
<h2><?php esc_html_e( 'Detailed Meaning', 'rx-theme' ); ?></h2>
<?php echo wp_kses_post( wpautop( $long_definition ) ); ?>
</div>
<?php endif; ?>
<?php if ( $specialty || $body_system || $difficulty ) : ?>
<div class="rx-glossary-medical-classification">
<h2><?php esc_html_e( 'Medical Classification', 'rx-theme' ); ?></h2>
<ul>
<?php if ( $specialty ) : ?>
<li><strong><?php esc_html_e( 'Specialty:', 'rx-theme' ); ?></strong> <?php echo esc_html( $specialty ); ?></li>
<?php endif; ?>
<?php if ( $body_system ) : ?>
<li><strong><?php esc_html_e( 'Body system:', 'rx-theme' ); ?></strong> <?php echo esc_html( $body_system ); ?></li>
<?php endif; ?>
<?php if ( $difficulty ) : ?>
<li><strong><?php esc_html_e( 'Reading level:', 'rx-theme' ); ?></strong> <?php echo esc_html( ucfirst( $difficulty ) ); ?></li>
<?php endif; ?>
</ul>
</div>
<?php endif; ?>
</div>
<?php
$before = ob_get_clean();
$after = '';
$faq_html = self::get_faq_html( $post_id );
if ( $faq_html ) {
$after .= $faq_html;
}
if ( $related_terms ) {
$after .= '<div class="rx-glossary-related-manual">';
$after .= '<h2>' . esc_html__( 'Related Terms', 'rx-theme' ) . '</h2>';
$after .= '<p>' . esc_html( $related_terms ) . '</p>';
$after .= '</div>';
}
$related_auto = self::get_related_glossary_html( $post_id, 6 );
if ( $related_auto ) {
$after .= $related_auto;
}
if ( $reviewed_by || $reviewed_date ) {
$after .= '<div class="rx-glossary-review-info">';
$after .= '<h2>' . esc_html__( 'Medical Review', 'rx-theme' ) . '</h2>';
if ( $reviewed_by ) {
$after .= '<p><strong>' . esc_html__( 'Reviewed by:', 'rx-theme' ) . '</strong> ' . esc_html( $reviewed_by ) . '</p>';
}
if ( $reviewed_date ) {
$after .= '<p><strong>' . esc_html__( 'Reviewed on:', 'rx-theme' ) . '</strong> ' . esc_html( $reviewed_date ) . '</p>';
}
$after .= '</div>';
}
if ( $references ) {
$after .= '<div class="rx-glossary-references">';
$after .= '<h2>' . esc_html__( 'References', 'rx-theme' ) . '</h2>';
$after .= wp_kses_post( wpautop( $references ) );
$after .= '</div>';
}
$after .= self::medical_disclaimer_html();
return $before . $content . $after;
}
/**
* FAQ HTML.
*/
public static function get_faq_html( $post_id ) {
$html = '';
for ( $i = 1; $i <= 3; $i++ ) {
$q = get_post_meta( $post_id, '_rx_glossary_faq_question_' . $i, true );
$a = get_post_meta( $post_id, '_rx_glossary_faq_answer_' . $i, true );
if ( $q && $a ) {
$html .= '<div class="rx-glossary-faq-item">';
$html .= '<h3>' . esc_html( $q ) . '</h3>';
$html .= wp_kses_post( wpautop( $a ) );
$html .= '</div>';
}
}
if ( ! $html ) {
return '';
}
return '<div class="rx-glossary-faq"><h2>' . esc_html__( 'Frequently Asked Questions', 'rx-theme' ) . '</h2>' . $html . '</div>';
}
/**
* Medical disclaimer.
*/
public static function medical_disclaimer_html() {
return '<div class="rx-medical-disclaimer rx-glossary-disclaimer">
<strong>' . esc_html__( 'Medical disclaimer:', 'rx-theme' ) . '</strong>
' . esc_html__( 'This glossary article is for general health education only. It does not replace medical advice, diagnosis, or treatment from a qualified healthcare professional.', 'rx-theme' ) . '
</div>';
}
/**
* Schema JSON-LD.
*/
public static function output_schema_json_ld() {
if ( ! is_singular( self::POST_TYPE ) ) {
return;
}
$post_id = get_the_ID();
$title = get_the_title( $post_id );
$url = get_permalink( $post_id );
$short_definition = get_post_meta( $post_id, '_rx_glossary_short_definition', true );
$long_definition = get_post_meta( $post_id, '_rx_glossary_long_definition', true );
$synonyms = get_post_meta( $post_id, '_rx_glossary_synonyms', true );
$specialty = get_post_meta( $post_id, '_rx_glossary_medical_specialty', true );
$reviewed_date = get_post_meta( $post_id, '_rx_glossary_reviewed_date', true );
$description = $short_definition ? $short_definition : wp_strip_all_tags( get_the_excerpt( $post_id ) );
if ( ! $description && $long_definition ) {
$description = wp_trim_words( wp_strip_all_tags( $long_definition ), 35 );
}
$schema = array(
'@context' => 'https://schema.org',
'@type' => array( 'DefinedTerm', 'MedicalEntity' ),
'name' => $title,
'description' => $description,
'url' => $url,
'inDefinedTermSet' => array(
'@type' => 'DefinedTermSet',
'name' => get_bloginfo( 'name' ) . ' Medical Glossary',
'url' => home_url( '/glossary/' ),
),
);
if ( $synonyms ) {
$schema['alternateName'] = array_map( 'trim', explode( ',', $synonyms ) );
}
if ( $specialty ) {
$schema['medicalSpecialty'] = $specialty;
}
if ( $reviewed_date ) {
$schema['dateReviewed'] = $reviewed_date;
}
$faq_schema = self::get_faq_schema( $post_id );
echo "\n<script type=\"application/ld+json\" class=\"rx-glossary-schema\">\n";
echo wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
echo "\n</script>\n";
if ( $faq_schema ) {
echo "\n<script type=\"application/ld+json\" class=\"rx-glossary-faq-schema\">\n";
echo wp_json_encode( $faq_schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT );
echo "\n</script>\n";
}
}
/**
* FAQ schema.
*/
public static function get_faq_schema( $post_id ) {
$items = array();
for ( $i = 1; $i <= 3; $i++ ) {
$q = get_post_meta( $post_id, '_rx_glossary_faq_question_' . $i, true );
$a = get_post_meta( $post_id, '_rx_glossary_faq_answer_' . $i, true );
if ( $q && $a ) {
$items[] = array(
'@type' => 'Question',
'name' => wp_strip_all_tags( $q ),
'acceptedAnswer' => array(
'@type' => 'Answer',
'text' => wp_strip_all_tags( $a ),
),
);
}
}
if ( empty( $items ) ) {
return false;
}
return array(
'@context' => 'https://schema.org',
'@type' => 'FAQPage',
'mainEntity' => $items,
);
}
/**
* Shortcode: [rx_glossary_index]
*/
public static function shortcode_glossary_index( $atts ) {
$atts = shortcode_atts(
array(
'letter' => '',
'category' => '',
'posts_per_page' => 100,
'show_letters' => 'yes',
'show_excerpt' => 'yes',
),
$atts,
'rx_glossary_index'
);
$args = array(
'post_type' => self::POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => absint( $atts['posts_per_page'] ),
'orderby' => 'title',
'order' => 'ASC',
);
$tax_query = array();
if ( ! empty( $atts['letter'] ) ) {
$tax_query[] = array(
'taxonomy' => self::TAX_LETTER,
'field' => 'slug',
'terms' => sanitize_title( $atts['letter'] ),
);
}
if ( ! empty( $atts['category'] ) ) {
$tax_query[] = array(
'taxonomy' => self::TAX_CATEGORY,
'field' => 'slug',
'terms' => sanitize_title( $atts['category'] ),
);
}
if ( $tax_query ) {
$args['tax_query'] = $tax_query;
}
$query = new WP_Query( $args );
ob_start();
echo '<div class="rx-glossary-index">';
if ( 'yes' === $atts['show_letters'] ) {
echo self::get_az_navigation_html();
}
if ( $query->have_posts() ) {
echo '<div class="rx-glossary-list">';
while ( $query->have_posts() ) {
$query->the_post();
echo '<article class="rx-glossary-list-item">';
echo '<h3><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></h3>';
if ( 'yes' === $atts['show_excerpt'] ) {
echo '<p>' . esc_html( self::get_glossary_excerpt( get_the_ID(), 24 ) ) . '</p>';
}
echo '</article>';
}
echo '</div>';
} else {
echo '<p>' . esc_html__( 'No glossary terms found.', 'rx-theme' ) . '</p>';
}
echo '</div>';
wp_reset_postdata();
return ob_get_clean();
}
/**
* Shortcode: [rx_glossary_search]
*/
public static function shortcode_glossary_search( $atts ) {
$atts = shortcode_atts(
array(
'placeholder' => __( 'Search medical glossary...', 'rx-theme' ),
'button' => __( 'Search', 'rx-theme' ),
),
$atts,
'rx_glossary_search'
);
$query = isset( $_GET['rx_glossary_s'] ) ? sanitize_text_field( wp_unslash( $_GET['rx_glossary_s'] ) ) : '';
ob_start();
?>
<div class="rx-glossary-search">
<form method="get" action="<?php echo esc_url( home_url( '/' ) ); ?>">
<input type="hidden" name="post_type" value="<?php echo esc_attr( self::POST_TYPE ); ?>">
<label class="screen-reader-text" for="rx_glossary_search_input"><?php esc_html_e( 'Search Glossary', 'rx-theme' ); ?></label>
<input id="rx_glossary_search_input" type="search" name="s" value="<?php echo esc_attr( $query ); ?>" placeholder="<?php echo esc_attr( $atts['placeholder'] ); ?>">
<button type="submit"><?php echo esc_html( $atts['button'] ); ?></button>
</form>
</div>
<?php
return ob_get_clean();
}
/**
* Shortcode: [rx_glossary_related]
*/
public static function shortcode_related_glossary( $atts ) {
$atts = shortcode_atts(
array(
'id' => get_the_ID(),
'limit' => 6,
),
$atts,
'rx_glossary_related'
);
return self::get_related_glossary_html( absint( $atts['id'] ), absint( $atts['limit'] ) );
}
/**
* Shortcode: [rx_glossary_featured]
*/
public static function shortcode_featured_glossary( $atts ) {
$atts = shortcode_atts(
array(
'limit' => 6,
),
$atts,
'rx_glossary_featured'
);
$query = new WP_Query(
array(
'post_type' => self::POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => absint( $atts['limit'] ),
'meta_key' => '_rx_glossary_featured',
'meta_value' => '1',
'orderby' => 'date',
'order' => 'DESC',
)
);
if ( ! $query->have_posts() ) {
return '';
}
ob_start();
echo '<div class="rx-featured-glossary">';
echo '<h2>' . esc_html__( 'Featured Glossary Terms', 'rx-theme' ) . '</h2>';
echo '<ul>';
while ( $query->have_posts() ) {
$query->the_post();
echo '<li><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></li>';
}
echo '</ul>';
echo '</div>';
wp_reset_postdata();
return ob_get_clean();
}
/**
* A-Z navigation HTML.
*/
public static function get_az_navigation_html() {
$letters = array_merge( range( 'A', 'Z' ), array( '0-9' ) );
$html = '<nav class="rx-glossary-az-nav" aria-label="' . esc_attr__( 'Glossary A to Z navigation', 'rx-theme' ) . '">';
foreach ( $letters as $letter ) {
$term = get_term_by( 'name', $letter, self::TAX_LETTER );
if ( $term && ! is_wp_error( $term ) ) {
$html .= '<a href="' . esc_url( get_term_link( $term ) ) . '">' . esc_html( $letter ) . '</a>';
} else {
$html .= '<span>' . esc_html( $letter ) . '</span>';
}
}
$html .= '</nav>';
return $html;
}
/**
* Related glossary HTML.
*/
public static function get_related_glossary_html( $post_id, $limit = 6 ) {
if ( ! $post_id ) {
return '';
}
$terms = wp_get_post_terms( $post_id, self::TAX_CATEGORY, array( 'fields' => 'ids' ) );
if ( empty( $terms ) || is_wp_error( $terms ) ) {
return '';
}
$query = new WP_Query(
array(
'post_type' => self::POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => absint( $limit ),
'post__not_in' => array( $post_id ),
'tax_query' => array(
array(
'taxonomy' => self::TAX_CATEGORY,
'field' => 'term_id',
'terms' => $terms,
),
),
'orderby' => 'rand',
)
);
if ( ! $query->have_posts() ) {
return '';
}
ob_start();
echo '<div class="rx-related-glossary">';
echo '<h2>' . esc_html__( 'Related Glossary Terms', 'rx-theme' ) . '</h2>';
echo '<ul>';
while ( $query->have_posts() ) {
$query->the_post();
echo '<li><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></li>';
}
echo '</ul>';
echo '</div>';
wp_reset_postdata();
return ob_get_clean();
}
/**
* Glossary excerpt helper.
*/
public static function get_glossary_excerpt( $post_id, $words = 25 ) {
$short_definition = get_post_meta( $post_id, '_rx_glossary_short_definition', true );
if ( $short_definition ) {
return wp_trim_words( wp_strip_all_tags( $short_definition ), $words );
}
$excerpt = get_the_excerpt( $post_id );
if ( $excerpt ) {
return wp_trim_words( wp_strip_all_tags( $excerpt ), $words );
}
$content = get_post_field( 'post_content', $post_id );
return wp_trim_words( wp_strip_all_tags( $content ), $words );
}
/**
* Template loader.
*/
public static function template_loader( $template ) {
if ( is_singular( self::POST_TYPE ) ) {
$theme_template = locate_template( array(
'single-rx_glossary.php',
'template-parts/glossary/single-glossary.php',
) );
if ( $theme_template ) {
return $theme_template;
}
}
if ( is_post_type_archive( self::POST_TYPE ) ) {
$theme_template = locate_template( array(
'archive-rx_glossary.php',
'template-parts/glossary/archive-glossary.php',
) );
if ( $theme_template ) {
return $theme_template;
}
}
if ( is_tax( array( self::TAX_CATEGORY, self::TAX_TAG, self::TAX_LETTER ) ) ) {
$theme_template = locate_template( array(
'taxonomy-rx_glossary.php',
'template-parts/glossary/taxonomy-glossary.php',
) );
if ( $theme_template ) {
return $theme_template;
}
}
return $template;
}
/**
* Body classes.
*/
public static function body_classes( $classes ) {
if ( is_singular( self::POST_TYPE ) ) {
$classes[] = 'rx-glossary-single';
}
if ( is_post_type_archive( self::POST_TYPE ) ) {
$classes[] = 'rx-glossary-archive';
}
if ( is_tax( array( self::TAX_CATEGORY, self::TAX_TAG, self::TAX_LETTER ) ) ) {
$classes[] = 'rx-glossary-taxonomy';
}
return $classes;
}
/**
* Enable sitemap.
*/
public static function enable_sitemap( $post_types ) {
$post_types[ self::POST_TYPE ] = get_post_type_object( self::POST_TYPE );
return $post_types;
}
/**
* AJAX live search.
*/
public static function ajax_live_search() {
check_ajax_referer( 'rx_glossary_live_search', 'nonce' );
$keyword = isset( $_GET['keyword'] ) ? sanitize_text_field( wp_unslash( $_GET['keyword'] ) ) : '';
if ( strlen( $keyword ) < 2 ) {
wp_send_json_success( array() );
}
$query = new WP_Query(
array(
'post_type' => self::POST_TYPE,
'post_status' => 'publish',
's' => $keyword,
'posts_per_page' => 10,
)
);
$results = array();
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$results[] = array(
'id' => get_the_ID(),
'title' => get_the_title(),
'url' => get_permalink(),
'excerpt' => self::get_glossary_excerpt( get_the_ID(), 18 ),
);
}
}
wp_reset_postdata();
wp_send_json_success( $results );
}
/**
* REST output enhancement.
*/
public static function rest_prepare_glossary( $response, $post, $request ) {
$post_id = $post->ID;
$response->data['rx_glossary_meta'] = array(
'short_definition' => get_post_meta( $post_id, '_rx_glossary_short_definition', true ),
'long_definition' => get_post_meta( $post_id, '_rx_glossary_long_definition', true ),
'pronunciation' => get_post_meta( $post_id, '_rx_glossary_pronunciation', true ),
'audio_url' => get_post_meta( $post_id, '_rx_glossary_audio_url', true ),
'synonyms' => get_post_meta( $post_id, '_rx_glossary_synonyms', true ),
'related_terms' => get_post_meta( $post_id, '_rx_glossary_related_terms', true ),
'medical_specialty' => get_post_meta( $post_id, '_rx_glossary_medical_specialty', true ),
'body_system' => get_post_meta( $post_id, '_rx_glossary_body_system', true ),
'difficulty' => get_post_meta( $post_id, '_rx_glossary_difficulty', true ),
'featured' => get_post_meta( $post_id, '_rx_glossary_featured', true ),
'reviewed_by' => get_post_meta( $post_id, '_rx_glossary_reviewed_by', true ),
'reviewed_date' => get_post_meta( $post_id, '_rx_glossary_reviewed_date', true ),
'references' => get_post_meta( $post_id, '_rx_glossary_source_references', true ),
);
$response->data['rx_reading_time'] = self::get_reading_time( $post_id );
return $response;
}
/**
* Auto assign first letter on transition.
*/
public static function auto_assign_first_letter( $new_status, $old_status, $post ) {
if ( self::POST_TYPE !== $post->post_type ) {
return;
}
if ( 'publish' === $new_status ) {
self::assign_first_letter( $post->ID );
}
}
/**
* Auto assign first letter on save.
*/
public static function auto_assign_first_letter_on_save( $post_id, $post, $update ) {
if ( self::POST_TYPE !== $post->post_type ) {
return;
}
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
return;
}
self::assign_first_letter( $post_id );
}
/**
* Assign first letter taxonomy.
*/
public static function assign_first_letter( $post_id ) {
$title = get_the_title( $post_id );
if ( ! $title ) {
return;
}
$first = strtoupper( mb_substr( trim( $title ), 0, 1 ) );
if ( preg_match( '/[A-Z]/', $first ) ) {
$letter = $first;
} else {
$letter = '0-9';
}
$term = get_term_by( 'name', $letter, self::TAX_LETTER );
if ( $term && ! is_wp_error( $term ) ) {
wp_set_object_terms( $post_id, array( (int) $term->term_id ), self::TAX_LETTER, false );
}
}
/**
* Improve glossary search by including selected meta fields.
*/
public static function improve_glossary_search( $search, $wp_query ) {
global $wpdb;
if ( is_admin() ) {
return $search;
}
if ( ! $wp_query->is_search() ) {
return $search;
}
$post_type = $wp_query->get( 'post_type' );
if ( self::POST_TYPE !== $post_type ) {
return $search;
}
$search_terms = $wp_query->get( 'search_terms' );
if ( empty( $search_terms ) ) {
return $search;
}
$like_parts = array();
foreach ( $search_terms as $term ) {
$like = '%' . $wpdb->esc_like( $term ) . '%';
$like_parts[] = $wpdb->prepare(
"({$wpdb->posts}.post_title LIKE %s OR {$wpdb->posts}.post_content LIKE %s OR EXISTS (
SELECT 1 FROM {$wpdb->postmeta}
WHERE {$wpdb->postmeta}.post_id = {$wpdb->posts}.ID
AND {$wpdb->postmeta}.meta_key IN (
'_rx_glossary_short_definition',
'_rx_glossary_long_definition',
'_rx_glossary_synonyms',
'_rx_glossary_related_terms',
'_rx_glossary_medical_specialty',
'_rx_glossary_body_system'
)
AND {$wpdb->postmeta}.meta_value LIKE %s
))",
$like,
$like,
$like
);
}
if ( $like_parts ) {
$search = ' AND ' . implode( ' AND ', $like_parts );
}
return $search;
}
/**
* Reading time.
*/
public static function get_reading_time( $post_id ) {
$content = get_post_field( 'post_content', $post_id );
$content .= ' ' . get_post_meta( $post_id, '_rx_glossary_short_definition', true );
$content .= ' ' . get_post_meta( $post_id, '_rx_glossary_long_definition', true );
$word_count = str_word_count( wp_strip_all_tags( $content ) );
$minutes = max( 1, ceil( $word_count / 220 ) );
return sprintf(
_n( '%s min read', '%s min read', $minutes, 'rx-theme' ),
number_format_i18n( $minutes )
);
}
/**
* Public helper: get glossary terms.
*/
public static function get_terms_query( $args = array() ) {
$defaults = array(
'post_type' => self::POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => 12,
'orderby' => 'title',
'order' => 'ASC',
);
return new WP_Query( wp_parse_args( $args, $defaults ) );
}
/**
* Public helper: get featured glossary terms.
*/
public static function get_featured_terms( $limit = 6 ) {
return self::get_terms_query(
array(
'posts_per_page' => absint( $limit ),
'meta_key' => '_rx_glossary_featured',
'meta_value' => '1',
)
);
}
/**
* Public helper: check glossary page.
*/
public static function is_glossary() {
return is_singular( self::POST_TYPE )
|| is_post_type_archive( self::POST_TYPE )
|| is_tax( array( self::TAX_CATEGORY, self::TAX_TAG, self::TAX_LETTER ) );
}
}
endif;
RX_Theme_Glossary::init();
/**
* Procedural helper functions for theme templates.
*/
if ( ! function_exists( 'rx_is_glossary' ) ) {
function rx_is_glossary() {
return RX_Theme_Glossary::is_glossary();
}
}
if ( ! function_exists( 'rx_get_glossary_excerpt' ) ) {
function rx_get_glossary_excerpt( $post_id = 0, $words = 25 ) {
$post_id = $post_id ? absint( $post_id ) : get_the_ID();
return RX_Theme_Glossary::get_glossary_excerpt( $post_id, $words );
}
}
if ( ! function_exists( 'rx_get_glossary_reading_time' ) ) {
function rx_get_glossary_reading_time( $post_id = 0 ) {
$post_id = $post_id ? absint( $post_id ) : get_the_ID();
return RX_Theme_Glossary::get_reading_time( $post_id );
}
}
if ( ! function_exists( 'rx_glossary_az_navigation' ) ) {
function rx_glossary_az_navigation() {
echo wp_kses_post( RX_Theme_Glossary::get_az_navigation_html() );
}
}
if ( ! function_exists( 'rx_get_related_glossary' ) ) {
function rx_get_related_glossary( $post_id = 0, $limit = 6 ) {
$post_id = $post_id ? absint( $post_id ) : get_the_ID();
return RX_Theme_Glossary::get_related_glossary_html( $post_id, $limit );
}
}
if ( ! function_exists( 'rx_related_glossary' ) ) {
function rx_related_glossary( $post_id = 0, $limit = 6 ) {
echo wp_kses_post( rx_get_related_glossary( $post_id, $limit ) );
}
}
if ( ! function_exists( 'rx_get_glossary_meta' ) ) {
function rx_get_glossary_meta( $key, $post_id = 0 ) {
$post_id = $post_id ? absint( $post_id ) : get_the_ID();
return get_post_meta( $post_id, $key, true );
}
}
After adding this file, go to:
WordPress Dashboard → Settings → Permalinks → Save Changes
This will refresh the rewrite rules so your glossary URLs work:
/glossary/
/glossary/neutropenia/
/glossary-category/hematology/
/glossary-letter/a/
Useful shortcodes:
[rx_glossary_index]
[rx_glossary_search]
[rx_glossary_featured limit="8"]
[rx_glossary_related limit="6"]
This file is already strong enough for your RX medical theme. Your next helpful files can be:
archive-rx_glossary.php
single-rx_glossary.php
taxonomy-rx_glossary_category.php
template-parts/glossary/card.php
assets/css/glossary.css
assets/js/glossary-live-search.js