I’ll build this as a strong reusable Reference/Citation block template for RX Theme: secure escaping, schema-ready HTML, flexible data sources, accessibility, copy/link actions, styling hooks, and developer filters.
Dear friend, create/replace this file:
/template-parts/blocks/block-reference.php
This is an advanced RX Theme medical reference / citation block with: manual references, post meta references, ACF support, DOI/PubMed/URL support, schema JSON-LD, copy citation button, search/filter box, collapsible list, external link security, accessibility, RTL support, print support, developer filters, and safe escaping.
<?php
/**
* RX Theme - Advanced Reference / Citation Block
*
* File: /template-parts/blocks/block-reference.php
*
* Usage:
* get_template_part(
* 'template-parts/blocks/block-reference',
* null,
* array(
* 'title' => 'References',
* 'description' => 'Evidence-based sources used in this article.',
* 'style' => 'numbered', // numbered|cards|compact|minimal
* 'columns' => 1, // 1|2
* 'search' => true,
* 'collapsible' => true,
* 'open' => true,
* 'schema' => true,
* 'references' => array(
* array(
* 'title' => 'Clinical practice guideline title',
* 'authors' => 'Author Name, Another Author',
* 'source' => 'Journal or Organization',
* 'year' => '2024',
* 'url' => 'https://example.com',
* 'doi' => '10.xxxx/example',
* 'pmid' => '12345678',
* 'accessed' => '2026-05-16',
* 'note' => 'Optional short note about the source.',
* ),
* ),
* )
* );
*
* Optional post meta formats supported:
* - rx_references as array
* - rx_references as JSON string
* - rx_references as newline-separated text
*
* Optional ACF field supported:
* - rx_references
*
* @package RX_Theme
*/
defined( 'ABSPATH' ) || exit;
$post_id = get_the_ID();
/**
* ------------------------------------------------------------
* 1. Default block settings
* ------------------------------------------------------------
*/
$defaults = array(
'id' => '',
'title' => esc_html__( 'References', 'rx-theme' ),
'description' => '',
'references' => array(),
'style' => 'numbered',
'columns' => 1,
'search' => true,
'collapsible' => true,
'open' => true,
'show_count' => true,
'show_source_type' => true,
'show_accessed_date' => true,
'show_copy_button' => true,
'show_print_button' => true,
'show_back_to_top' => false,
'schema' => true,
'empty_message' => esc_html__( 'No references have been added yet.', 'rx-theme' ),
'class' => '',
'anchor' => '',
'aria_label' => '',
'allowed_html_note' => array(
'a' => array(
'href' => true,
'title' => true,
'target' => true,
'rel' => true,
),
'strong' => array(),
'em' => array(),
'br' => array(),
'span' => array(
'class' => true,
),
),
);
$args = isset( $args ) && is_array( $args ) ? wp_parse_args( $args, $defaults ) : $defaults;
/**
* Developer filter:
* Allows global modification of reference block args.
*/
$args = apply_filters( 'rx_theme_reference_block_args', $args, $post_id );
/**
* ------------------------------------------------------------
* 2. Helper functions as local closures
* ------------------------------------------------------------
*/
$rx_bool = static function ( $value ) {
return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
};
$rx_clean_text = static function ( $value ) {
if ( is_array( $value ) || is_object( $value ) ) {
return '';
}
return trim( wp_strip_all_tags( (string) $value ) );
};
$rx_clean_html = static function ( $value, $allowed_html ) {
if ( is_array( $value ) || is_object( $value ) ) {
return '';
}
return wp_kses( (string) $value, $allowed_html );
};
$rx_clean_url = static function ( $url ) {
$url = trim( (string) $url );
if ( empty( $url ) ) {
return '';
}
return esc_url_raw( $url );
};
$rx_is_external_url = static function ( $url ) {
if ( empty( $url ) ) {
return false;
}
$home_host = wp_parse_url( home_url(), PHP_URL_HOST );
$url_host = wp_parse_url( $url, PHP_URL_HOST );
if ( empty( $home_host ) || empty( $url_host ) ) {
return false;
}
return strtolower( $home_host ) !== strtolower( $url_host );
};
$rx_normalize_doi = static function ( $doi ) {
$doi = trim( (string) $doi );
if ( empty( $doi ) ) {
return '';
}
$doi = preg_replace( '#^https?://(dx\.)?doi\.org/#i', '', $doi );
$doi = preg_replace( '#^doi:\s*#i', '', $doi );
return trim( $doi );
};
$rx_doi_url = static function ( $doi ) use ( $rx_normalize_doi ) {
$doi = $rx_normalize_doi( $doi );
if ( empty( $doi ) ) {
return '';
}
return 'https://doi.org/' . rawurlencode( $doi );
};
$rx_pubmed_url = static function ( $pmid ) {
$pmid = preg_replace( '/[^0-9]/', '', (string) $pmid );
if ( empty( $pmid ) ) {
return '';
}
return 'https://pubmed.ncbi.nlm.nih.gov/' . $pmid . '/';
};
$rx_guess_source_type = static function ( $reference ) {
$type = '';
if ( ! empty( $reference['type'] ) ) {
$type = $reference['type'];
} elseif ( ! empty( $reference['doi'] ) || ! empty( $reference['pmid'] ) ) {
$type = 'Journal';
} elseif ( ! empty( $reference['publisher'] ) ) {
$type = 'Publisher';
} elseif ( ! empty( $reference['source'] ) ) {
$type = 'Source';
} elseif ( ! empty( $reference['url'] ) ) {
$type = 'Web';
}
return $type;
};
$rx_build_citation_text = static function ( $reference ) {
$parts = array();
if ( ! empty( $reference['authors'] ) ) {
$parts[] = $reference['authors'];
}
if ( ! empty( $reference['year'] ) ) {
$parts[] = '(' . $reference['year'] . ')';
}
if ( ! empty( $reference['title'] ) ) {
$parts[] = $reference['title'];
}
if ( ! empty( $reference['source'] ) ) {
$parts[] = $reference['source'];
}
if ( ! empty( $reference['volume'] ) ) {
$parts[] = 'Vol. ' . $reference['volume'];
}
if ( ! empty( $reference['issue'] ) ) {
$parts[] = 'Issue ' . $reference['issue'];
}
if ( ! empty( $reference['pages'] ) ) {
$parts[] = 'Pages ' . $reference['pages'];
}
if ( ! empty( $reference['doi'] ) ) {
$parts[] = 'DOI: ' . $reference['doi'];
}
if ( ! empty( $reference['pmid'] ) ) {
$parts[] = 'PMID: ' . $reference['pmid'];
}
return trim( implode( '. ', array_filter( $parts ) ) );
};
$rx_parse_text_references = static function ( $raw ) {
$items = array();
if ( empty( $raw ) || ! is_string( $raw ) ) {
return $items;
}
$lines = preg_split( '/\r\n|\r|\n/', $raw );
foreach ( $lines as $line ) {
$line = trim( $line );
if ( empty( $line ) ) {
continue;
}
$url = '';
if ( preg_match( '#https?://[^\s]+#i', $line, $matches ) ) {
$url = $matches[0];
}
$items[] = array(
'title' => $line,
'url' => $url,
);
}
return $items;
};
$rx_get_meta_references = static function ( $post_id ) use ( $rx_parse_text_references ) {
$references = array();
if ( ! $post_id ) {
return $references;
}
$meta = get_post_meta( $post_id, 'rx_references', true );
if ( empty( $meta ) ) {
return $references;
}
if ( is_array( $meta ) ) {
return $meta;
}
if ( is_string( $meta ) ) {
$json = json_decode( $meta, true );
if ( JSON_ERROR_NONE === json_last_error() && is_array( $json ) ) {
return $json;
}
return $rx_parse_text_references( $meta );
}
return $references;
};
$rx_get_acf_references = static function ( $post_id ) {
if ( ! function_exists( 'get_field' ) || ! $post_id ) {
return array();
}
$acf_refs = get_field( 'rx_references', $post_id );
return is_array( $acf_refs ) ? $acf_refs : array();
};
$rx_normalize_reference = static function ( $reference, $index ) use ( $rx_clean_text, $rx_clean_url, $rx_normalize_doi, $rx_guess_source_type ) {
if ( is_string( $reference ) ) {
$reference = array(
'title' => $reference,
);
}
if ( ! is_array( $reference ) ) {
return array();
}
$url = ! empty( $reference['url'] ) ? $rx_clean_url( $reference['url'] ) : '';
$doi = ! empty( $reference['doi'] ) ? $rx_normalize_doi( $reference['doi'] ) : '';
$pmid = ! empty( $reference['pmid'] ) ? preg_replace( '/[^0-9]/', '', (string) $reference['pmid'] ) : '';
$title = ! empty( $reference['title'] ) ? $rx_clean_text( $reference['title'] ) : '';
$authors = ! empty( $reference['authors'] ) ? $rx_clean_text( $reference['authors'] ) : '';
$source = ! empty( $reference['source'] ) ? $rx_clean_text( $reference['source'] ) : '';
$year = ! empty( $reference['year'] ) ? $rx_clean_text( $reference['year'] ) : '';
$note = ! empty( $reference['note'] ) ? (string) $reference['note'] : '';
$type = $rx_guess_source_type( $reference );
$priority = isset( $reference['priority'] ) ? absint( $reference['priority'] ) : 10;
if ( empty( $title ) && ! empty( $url ) ) {
$title = $url;
}
if ( empty( $title ) && empty( $url ) && empty( $doi ) && empty( $pmid ) ) {
return array();
}
return array(
'id' => ! empty( $reference['id'] ) ? sanitize_html_class( $reference['id'] ) : 'rx-ref-' . ( $index + 1 ),
'title' => $title,
'authors' => $authors,
'source' => $source,
'publisher' => ! empty( $reference['publisher'] ) ? $rx_clean_text( $reference['publisher'] ) : '',
'year' => $year,
'date' => ! empty( $reference['date'] ) ? $rx_clean_text( $reference['date'] ) : '',
'accessed' => ! empty( $reference['accessed'] ) ? $rx_clean_text( $reference['accessed'] ) : '',
'volume' => ! empty( $reference['volume'] ) ? $rx_clean_text( $reference['volume'] ) : '',
'issue' => ! empty( $reference['issue'] ) ? $rx_clean_text( $reference['issue'] ) : '',
'pages' => ! empty( $reference['pages'] ) ? $rx_clean_text( $reference['pages'] ) : '',
'url' => $url,
'doi' => $doi,
'pmid' => $pmid,
'type' => $rx_clean_text( $type ),
'note' => $note,
'priority' => $priority,
);
};
/**
* ------------------------------------------------------------
* 3. Collect references from args, ACF, and post meta
* ------------------------------------------------------------
*/
$references = array();
if ( ! empty( $args['references'] ) && is_array( $args['references'] ) ) {
$references = array_merge( $references, $args['references'] );
}
$acf_references = $rx_get_acf_references( $post_id );
if ( ! empty( $acf_references ) ) {
$references = array_merge( $references, $acf_references );
}
$meta_references = $rx_get_meta_references( $post_id );
if ( ! empty( $meta_references ) ) {
$references = array_merge( $references, $meta_references );
}
/**
* Developer filter:
* Modify raw references before normalization.
*/
$references = apply_filters( 'rx_theme_reference_block_raw_references', $references, $post_id, $args );
/**
* Normalize references.
*/
$normalized_references = array();
foreach ( $references as $index => $reference ) {
$item = $rx_normalize_reference( $reference, $index );
if ( ! empty( $item ) ) {
$normalized_references[] = $item;
}
}
/**
* Remove duplicate references by URL, DOI, PMID, or title.
*/
$seen = array();
$references = array();
foreach ( $normalized_references as $reference ) {
$key_parts = array(
! empty( $reference['doi'] ) ? 'doi:' . strtolower( $reference['doi'] ) : '',
! empty( $reference['pmid'] ) ? 'pmid:' . strtolower( $reference['pmid'] ) : '',
! empty( $reference['url'] ) ? 'url:' . strtolower( $reference['url'] ) : '',
! empty( $reference['title'] ) ? 'title:' . strtolower( $reference['title'] ) : '',
);
$key_parts = array_filter( $key_parts );
$key = md5( implode( '|', $key_parts ) );
if ( isset( $seen[ $key ] ) ) {
continue;
}
$seen[ $key ] = true;
$references[] = $reference;
}
/**
* Sort by priority, then year desc if available.
*/
usort(
$references,
static function ( $a, $b ) {
$priority_a = isset( $a['priority'] ) ? absint( $a['priority'] ) : 10;
$priority_b = isset( $b['priority'] ) ? absint( $b['priority'] ) : 10;
if ( $priority_a !== $priority_b ) {
return $priority_a <=> $priority_b;
}
$year_a = ! empty( $a['year'] ) ? absint( $a['year'] ) : 0;
$year_b = ! empty( $b['year'] ) ? absint( $b['year'] ) : 0;
return $year_b <=> $year_a;
}
);
/**
* Developer filter:
* Modify final normalized references.
*/
$references = apply_filters( 'rx_theme_reference_block_references', $references, $post_id, $args );
/**
* ------------------------------------------------------------
* 4. Prepare block variables
* ------------------------------------------------------------
*/
$block_id = ! empty( $args['id'] )
? sanitize_html_class( $args['id'] )
: 'rx-reference-block-' . wp_unique_id();
$anchor = ! empty( $args['anchor'] )
? sanitize_title( $args['anchor'] )
: $block_id;
$title = $rx_clean_text( $args['title'] );
$description = $rx_clean_html( $args['description'], $args['allowed_html_note'] );
$style = sanitize_html_class( $args['style'] );
$columns = absint( $args['columns'] );
$columns = in_array( $columns, array( 1, 2 ), true ) ? $columns : 1;
$reference_count = count( $references );
$has_references = $reference_count > 0;
$is_collapsible = $rx_bool( $args['collapsible'] );
$is_open = $rx_bool( $args['open'] );
$show_search = $rx_bool( $args['search'] ) && $has_references;
$show_count = $rx_bool( $args['show_count'] );
$show_copy_button = $rx_bool( $args['show_copy_button'] );
$show_print_button = $rx_bool( $args['show_print_button'] );
$show_source_type = $rx_bool( $args['show_source_type'] );
$show_accessed_date = $rx_bool( $args['show_accessed_date'] );
$show_back_to_top = $rx_bool( $args['show_back_to_top'] );
$schema_enabled = $rx_bool( $args['schema'] );
$aria_label = ! empty( $args['aria_label'] ) ? $rx_clean_text( $args['aria_label'] ) : $title;
$block_classes = array(
'rx-reference-block',
'rx-reference-block--' . $style,
'rx-reference-block--columns-' . $columns,
$has_references ? 'has-references' : 'has-no-references',
$is_collapsible ? 'is-collapsible' : 'is-static',
! empty( $args['class'] ) ? sanitize_html_class( $args['class'] ) : '',
);
$block_classes = array_filter( $block_classes );
/**
* ------------------------------------------------------------
* 5. Add one-time CSS and JS
* ------------------------------------------------------------
*/
if ( ! defined( 'RX_REFERENCE_BLOCK_ASSETS_LOADED' ) ) {
define( 'RX_REFERENCE_BLOCK_ASSETS_LOADED', true );
?>
<style id="rx-reference-block-style">
.rx-reference-block {
--rx-ref-bg: var(--rx-color-surface, #ffffff);
--rx-ref-text: var(--rx-color-text, #172033);
--rx-ref-muted: var(--rx-color-muted, #667085);
--rx-ref-border: var(--rx-color-border, #e5e7eb);
--rx-ref-accent: var(--rx-color-primary, #2563eb);
--rx-ref-accent-soft: color-mix(in srgb, var(--rx-ref-accent) 10%, transparent);
--rx-ref-radius: var(--rx-radius-lg, 14px);
--rx-ref-shadow: var(--rx-shadow-sm, 0 4px 18px rgba(15, 23, 42, 0.06));
background: var(--rx-ref-bg);
color: var(--rx-ref-text);
border: 1px solid var(--rx-ref-border);
border-radius: var(--rx-ref-radius);
box-shadow: var(--rx-ref-shadow);
margin: 2rem 0;
overflow: hidden;
}
.rx-reference-block * {
box-sizing: border-box;
}
.rx-reference-block__header {
padding: 1.25rem 1.25rem 1rem;
border-bottom: 1px solid var(--rx-ref-border);
background: linear-gradient(180deg, var(--rx-ref-accent-soft), transparent);
}
.rx-reference-block__top {
display: flex;
gap: 1rem;
align-items: center;
justify-content: space-between;
}
.rx-reference-block__title {
margin: 0;
font-size: clamp(1.15rem, 2vw, 1.5rem);
line-height: 1.25;
font-weight: 700;
}
.rx-reference-block__count {
display: inline-flex;
align-items: center;
gap: 0.35rem;
white-space: nowrap;
font-size: 0.8125rem;
font-weight: 700;
color: var(--rx-ref-accent);
background: var(--rx-ref-accent-soft);
border: 1px solid color-mix(in srgb, var(--rx-ref-accent) 25%, transparent);
border-radius: 999px;
padding: 0.35rem 0.65rem;
}
.rx-reference-block__description {
margin: 0.65rem 0 0;
color: var(--rx-ref-muted);
font-size: 0.95rem;
line-height: 1.65;
}
.rx-reference-block__tools {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.65rem;
padding: 1rem 1.25rem;
border-bottom: 1px solid var(--rx-ref-border);
}
.rx-reference-block__search {
flex: 1 1 260px;
min-width: 0;
}
.rx-reference-block__search-input {
width: 100%;
border: 1px solid var(--rx-ref-border);
border-radius: 999px;
padding: 0.7rem 1rem;
font: inherit;
color: var(--rx-ref-text);
background: #fff;
outline: none;
}
.rx-reference-block__search-input:focus {
border-color: var(--rx-ref-accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--rx-ref-accent) 18%, transparent);
}
.rx-reference-block__button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.35rem;
border: 1px solid var(--rx-ref-border);
border-radius: 999px;
background: #fff;
color: var(--rx-ref-text);
padding: 0.65rem 0.9rem;
font: inherit;
font-size: 0.875rem;
font-weight: 700;
line-height: 1;
cursor: pointer;
text-decoration: none;
}
.rx-reference-block__button:hover,
.rx-reference-block__button:focus {
border-color: var(--rx-ref-accent);
color: var(--rx-ref-accent);
outline: none;
}
.rx-reference-block__content {
padding: 1.25rem;
}
.rx-reference-block__list {
margin: 0;
padding-left: 1.25rem;
}
.rx-reference-block--cards .rx-reference-block__list,
.rx-reference-block--compact .rx-reference-block__list,
.rx-reference-block--minimal .rx-reference-block__list {
list-style: none;
padding-left: 0;
}
.rx-reference-block--columns-2 .rx-reference-block__list {
column-count: 2;
column-gap: 2rem;
}
.rx-reference-block__item {
break-inside: avoid;
margin: 0 0 1rem;
padding: 1rem;
border: 1px solid var(--rx-ref-border);
border-radius: 12px;
background: color-mix(in srgb, var(--rx-ref-bg) 96%, var(--rx-ref-accent) 4%);
}
.rx-reference-block--numbered .rx-reference-block__item {
padding-left: 1rem;
}
.rx-reference-block--compact .rx-reference-block__item {
padding: 0.75rem 0;
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
}
.rx-reference-block--minimal .rx-reference-block__item {
padding: 0.35rem 0;
border: 0;
background: transparent;
}
.rx-reference-block__citation {
display: grid;
gap: 0.45rem;
}
.rx-reference-block__main {
font-size: 0.96rem;
line-height: 1.65;
}
.rx-reference-block__main strong {
color: var(--rx-ref-text);
}
.rx-reference-block__meta {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
align-items: center;
color: var(--rx-ref-muted);
font-size: 0.8125rem;
line-height: 1.5;
}
.rx-reference-block__chip {
display: inline-flex;
align-items: center;
gap: 0.25rem;
border: 1px solid var(--rx-ref-border);
border-radius: 999px;
padding: 0.2rem 0.55rem;
background: #fff;
color: var(--rx-ref-muted);
text-decoration: none;
}
.rx-reference-block__chip[href]:hover,
.rx-reference-block__chip[href]:focus {
color: var(--rx-ref-accent);
border-color: var(--rx-ref-accent);
outline: none;
}
.rx-reference-block__note {
color: var(--rx-ref-muted);
font-size: 0.875rem;
line-height: 1.65;
}
.rx-reference-block__links {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
margin-top: 0.25rem;
}
.rx-reference-block__link {
display: inline-flex;
align-items: center;
gap: 0.25rem;
color: var(--rx-ref-accent);
font-size: 0.85rem;
font-weight: 700;
text-decoration: none;
}
.rx-reference-block__link:hover,
.rx-reference-block__link:focus {
text-decoration: underline;
outline: none;
}
.rx-reference-block__empty {
margin: 0;
color: var(--rx-ref-muted);
font-size: 0.95rem;
}
.rx-reference-block__no-results {
display: none;
margin: 0;
color: var(--rx-ref-muted);
font-size: 0.95rem;
}
.rx-reference-block.has-filtered-empty .rx-reference-block__no-results {
display: block;
}
.rx-reference-block.has-filtered-empty .rx-reference-block__list {
display: none;
}
.rx-reference-block__toggle {
background: transparent;
border: 0;
color: var(--rx-ref-accent);
font: inherit;
font-weight: 700;
cursor: pointer;
padding: 0.25rem;
}
.rx-reference-block__toggle:focus {
outline: 2px solid var(--rx-ref-accent);
outline-offset: 3px;
border-radius: 6px;
}
.rx-reference-block mark {
background: #fff3bf;
color: inherit;
padding: 0 0.15em;
border-radius: 0.2em;
}
@media (max-width: 720px) {
.rx-reference-block--columns-2 .rx-reference-block__list {
column-count: 1;
}
.rx-reference-block__top {
align-items: flex-start;
flex-direction: column;
}
.rx-reference-block__tools {
align-items: stretch;
flex-direction: column;
}
.rx-reference-block__button {
width: 100%;
}
}
@media print {
.rx-reference-block {
box-shadow: none;
border-color: #999;
}
.rx-reference-block__tools,
.rx-reference-block__button,
.rx-reference-block__toggle {
display: none !important;
}
.rx-reference-block__content {
display: block !important;
}
.rx-reference-block__link::after {
content: " (" attr(href) ")";
font-weight: 400;
color: #333;
}
}
</style>
<script id="rx-reference-block-script">
document.addEventListener('DOMContentLoaded', function () {
var blocks = document.querySelectorAll('.rx-reference-block');
blocks.forEach(function (block) {
var searchInput = block.querySelector('[data-rx-reference-search]');
var listItems = block.querySelectorAll('[data-rx-reference-item]');
var toggleButton = block.querySelector('[data-rx-reference-toggle]');
var content = block.querySelector('[data-rx-reference-content]');
var printButton = block.querySelector('[data-rx-reference-print]');
var copyButtons = block.querySelectorAll('[data-rx-reference-copy]');
if (searchInput) {
searchInput.addEventListener('input', function () {
var query = searchInput.value.toLowerCase().trim();
var visibleCount = 0;
listItems.forEach(function (item) {
var text = item.getAttribute('data-rx-reference-text') || item.textContent || '';
var isVisible = !query || text.toLowerCase().indexOf(query) !== -1;
item.hidden = !isVisible;
if (isVisible) {
visibleCount++;
}
});
if (query && visibleCount === 0) {
block.classList.add('has-filtered-empty');
} else {
block.classList.remove('has-filtered-empty');
}
});
}
if (toggleButton && content) {
toggleButton.addEventListener('click', function () {
var expanded = toggleButton.getAttribute('aria-expanded') === 'true';
toggleButton.setAttribute('aria-expanded', expanded ? 'false' : 'true');
content.hidden = expanded;
toggleButton.textContent = expanded
? toggleButton.getAttribute('data-label-show')
: toggleButton.getAttribute('data-label-hide');
});
}
if (printButton) {
printButton.addEventListener('click', function () {
window.print();
});
}
copyButtons.forEach(function (button) {
button.addEventListener('click', function () {
var text = button.getAttribute('data-citation') || '';
if (!text) {
return;
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function () {
var oldText = button.textContent;
button.textContent = button.getAttribute('data-label-copied') || 'Copied';
setTimeout(function () {
button.textContent = oldText;
}, 1600);
});
}
});
});
});
});
</script>
<?php
}
/**
* ------------------------------------------------------------
* 6. Schema JSON-LD
* ------------------------------------------------------------
*/
if ( $schema_enabled && $has_references ) {
$schema_items = array();
foreach ( $references as $position => $reference ) {
$item_url = '';
if ( ! empty( $reference['url'] ) ) {
$item_url = $reference['url'];
} elseif ( ! empty( $reference['doi'] ) ) {
$item_url = $rx_doi_url( $reference['doi'] );
} elseif ( ! empty( $reference['pmid'] ) ) {
$item_url = $rx_pubmed_url( $reference['pmid'] );
}
$schema_items[] = array_filter(
array(
'@type' => 'ListItem',
'position' => $position + 1,
'item' => array_filter(
array(
'@type' => 'CreativeWork',
'name' => $reference['title'],
'url' => $item_url,
'author' => $reference['authors'],
'publisher' => $reference['publisher'],
'datePublished' => ! empty( $reference['date'] ) ? $reference['date'] : $reference['year'],
'isPartOf' => $reference['source'],
'identifier' => array_filter(
array(
! empty( $reference['doi'] ) ? 'doi:' . $reference['doi'] : '',
! empty( $reference['pmid'] ) ? 'pmid:' . $reference['pmid'] : '',
)
),
)
),
)
);
}
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'ItemList',
'name' => $title,
'numberOfItems' => $reference_count,
'itemListElement' => $schema_items,
);
$schema = apply_filters( 'rx_theme_reference_block_schema', $schema, $references, $post_id, $args );
echo '<script type="application/ld+json">' . wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>' . "\n";
}
?>
<section
id="<?php echo esc_attr( $anchor ); ?>"
class="<?php echo esc_attr( implode( ' ', $block_classes ) ); ?>"
aria-label="<?php echo esc_attr( $aria_label ); ?>"
data-rx-reference-block
>
<header class="rx-reference-block__header">
<div class="rx-reference-block__top">
<?php if ( ! empty( $title ) ) : ?>
<h2 class="rx-reference-block__title">
<?php echo esc_html( $title ); ?>
</h2>
<?php endif; ?>
<?php if ( $show_count && $has_references ) : ?>
<span class="rx-reference-block__count" aria-label="<?php echo esc_attr( sprintf( _n( '%s reference', '%s references', $reference_count, 'rx-theme' ), number_format_i18n( $reference_count ) ) ); ?>">
<?php echo esc_html( number_format_i18n( $reference_count ) ); ?>
<?php echo esc_html( _n( 'Reference', 'References', $reference_count, 'rx-theme' ) ); ?>
</span>
<?php endif; ?>
</div>
<?php if ( ! empty( $description ) ) : ?>
<div class="rx-reference-block__description">
<?php echo $description; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php endif; ?>
</header>
<?php if ( $has_references && ( $show_search || $show_print_button || $is_collapsible ) ) : ?>
<div class="rx-reference-block__tools">
<?php if ( $show_search ) : ?>
<div class="rx-reference-block__search">
<label class="screen-reader-text" for="<?php echo esc_attr( $block_id ); ?>-search">
<?php esc_html_e( 'Search references', 'rx-theme' ); ?>
</label>
<input
id="<?php echo esc_attr( $block_id ); ?>-search"
class="rx-reference-block__search-input"
type="search"
placeholder="<?php echo esc_attr__( 'Search references...', 'rx-theme' ); ?>"
autocomplete="off"
data-rx-reference-search
>
</div>
<?php endif; ?>
<?php if ( $is_collapsible ) : ?>
<button
type="button"
class="rx-reference-block__button"
aria-expanded="<?php echo $is_open ? 'true' : 'false'; ?>"
aria-controls="<?php echo esc_attr( $block_id ); ?>-content"
data-rx-reference-toggle
data-label-show="<?php echo esc_attr__( 'Show references', 'rx-theme' ); ?>"
data-label-hide="<?php echo esc_attr__( 'Hide references', 'rx-theme' ); ?>"
>
<?php echo esc_html( $is_open ? __( 'Hide references', 'rx-theme' ) : __( 'Show references', 'rx-theme' ) ); ?>
</button>
<?php endif; ?>
<?php if ( $show_print_button ) : ?>
<button type="button" class="rx-reference-block__button" data-rx-reference-print>
<?php esc_html_e( 'Print', 'rx-theme' ); ?>
</button>
<?php endif; ?>
</div>
<?php endif; ?>
<div
id="<?php echo esc_attr( $block_id ); ?>-content"
class="rx-reference-block__content"
data-rx-reference-content
<?php echo ( $is_collapsible && ! $is_open ) ? 'hidden' : ''; ?>
>
<?php if ( ! $has_references ) : ?>
<p class="rx-reference-block__empty">
<?php echo esc_html( $args['empty_message'] ); ?>
</p>
<?php else : ?>
<p class="rx-reference-block__no-results" role="status">
<?php esc_html_e( 'No matching references found.', 'rx-theme' ); ?>
</p>
<ol class="rx-reference-block__list">
<?php foreach ( $references as $index => $reference ) : ?>
<?php
$citation_text = $rx_build_citation_text( $reference );
$search_text = strtolower(
implode(
' ',
array_filter(
array(
$reference['title'],
$reference['authors'],
$reference['source'],
$reference['publisher'],
$reference['year'],
$reference['doi'],
$reference['pmid'],
wp_strip_all_tags( $reference['note'] ),
)
)
)
);
$main_url = '';
if ( ! empty( $reference['url'] ) ) {
$main_url = $reference['url'];
} elseif ( ! empty( $reference['doi'] ) ) {
$main_url = $rx_doi_url( $reference['doi'] );
} elseif ( ! empty( $reference['pmid'] ) ) {
$main_url = $rx_pubmed_url( $reference['pmid'] );
}
$is_external = $rx_is_external_url( $main_url );
$link_rel = $is_external ? 'nofollow noopener noreferrer external' : 'bookmark';
$link_target = $is_external ? '_blank' : '_self';
?>
<li
id="<?php echo esc_attr( $reference['id'] ); ?>"
class="rx-reference-block__item"
data-rx-reference-item
data-rx-reference-text="<?php echo esc_attr( $search_text ); ?>"
>
<article class="rx-reference-block__citation" itemscope itemtype="https://schema.org/CreativeWork">
<div class="rx-reference-block__main">
<?php if ( ! empty( $reference['authors'] ) ) : ?>
<span itemprop="author"><?php echo esc_html( $reference['authors'] ); ?></span>.
<?php endif; ?>
<?php if ( ! empty( $reference['year'] ) ) : ?>
<span itemprop="datePublished">(<?php echo esc_html( $reference['year'] ); ?>)</span>.
<?php endif; ?>
<?php if ( ! empty( $reference['title'] ) ) : ?>
<strong itemprop="name">
<?php if ( ! empty( $main_url ) ) : ?>
<a
class="rx-reference-block__link"
href="<?php echo esc_url( $main_url ); ?>"
target="<?php echo esc_attr( $link_target ); ?>"
rel="<?php echo esc_attr( $link_rel ); ?>"
itemprop="url"
>
<?php echo esc_html( $reference['title'] ); ?>
</a>
<?php else : ?>
<?php echo esc_html( $reference['title'] ); ?>
<?php endif; ?>
</strong>.
<?php endif; ?>
<?php if ( ! empty( $reference['source'] ) ) : ?>
<em itemprop="isPartOf"><?php echo esc_html( $reference['source'] ); ?></em>.
<?php endif; ?>
<?php if ( ! empty( $reference['publisher'] ) ) : ?>
<span itemprop="publisher"><?php echo esc_html( $reference['publisher'] ); ?></span>.
<?php endif; ?>
</div>
<div class="rx-reference-block__meta">
<?php if ( $show_source_type && ! empty( $reference['type'] ) ) : ?>
<span class="rx-reference-block__chip">
<?php echo esc_html( $reference['type'] ); ?>
</span>
<?php endif; ?>
<?php if ( ! empty( $reference['volume'] ) ) : ?>
<span class="rx-reference-block__chip">
<?php echo esc_html__( 'Vol.', 'rx-theme' ); ?>
<?php echo esc_html( $reference['volume'] ); ?>
</span>
<?php endif; ?>
<?php if ( ! empty( $reference['issue'] ) ) : ?>
<span class="rx-reference-block__chip">
<?php echo esc_html__( 'Issue', 'rx-theme' ); ?>
<?php echo esc_html( $reference['issue'] ); ?>
</span>
<?php endif; ?>
<?php if ( ! empty( $reference['pages'] ) ) : ?>
<span class="rx-reference-block__chip">
<?php echo esc_html__( 'Pages', 'rx-theme' ); ?>
<?php echo esc_html( $reference['pages'] ); ?>
</span>
<?php endif; ?>
<?php if ( $show_accessed_date && ! empty( $reference['accessed'] ) ) : ?>
<span class="rx-reference-block__chip">
<?php echo esc_html__( 'Accessed:', 'rx-theme' ); ?>
<?php echo esc_html( $reference['accessed'] ); ?>
</span>
<?php endif; ?>
</div>
<?php if ( ! empty( $reference['note'] ) ) : ?>
<div class="rx-reference-block__note">
<?php echo $rx_clean_html( $reference['note'], $args['allowed_html_note'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php endif; ?>
<div class="rx-reference-block__links">
<?php if ( ! empty( $reference['url'] ) ) : ?>
<a
class="rx-reference-block__chip"
href="<?php echo esc_url( $reference['url'] ); ?>"
target="<?php echo esc_attr( $rx_is_external_url( $reference['url'] ) ? '_blank' : '_self' ); ?>"
rel="<?php echo esc_attr( $rx_is_external_url( $reference['url'] ) ? 'nofollow noopener noreferrer external' : 'bookmark' ); ?>"
>
<?php esc_html_e( 'Source link', 'rx-theme' ); ?>
</a>
<?php endif; ?>
<?php if ( ! empty( $reference['doi'] ) ) : ?>
<a
class="rx-reference-block__chip"
href="<?php echo esc_url( $rx_doi_url( $reference['doi'] ) ); ?>"
target="_blank"
rel="nofollow noopener noreferrer external"
>
<?php esc_html_e( 'DOI', 'rx-theme' ); ?>:
<?php echo esc_html( $reference['doi'] ); ?>
</a>
<?php endif; ?>
<?php if ( ! empty( $reference['pmid'] ) ) : ?>
<a
class="rx-reference-block__chip"
href="<?php echo esc_url( $rx_pubmed_url( $reference['pmid'] ) ); ?>"
target="_blank"
rel="nofollow noopener noreferrer external"
>
<?php esc_html_e( 'PubMed', 'rx-theme' ); ?>:
<?php echo esc_html( $reference['pmid'] ); ?>
</a>
<?php endif; ?>
<?php if ( $show_copy_button && ! empty( $citation_text ) ) : ?>
<button
type="button"
class="rx-reference-block__chip rx-reference-block__copy"
data-rx-reference-copy
data-citation="<?php echo esc_attr( $citation_text ); ?>"
data-label-copied="<?php echo esc_attr__( 'Copied', 'rx-theme' ); ?>"
>
<?php esc_html_e( 'Copy citation', 'rx-theme' ); ?>
</button>
<?php endif; ?>
</div>
</article>
</li>
<?php endforeach; ?>
</ol>
<?php if ( $show_back_to_top ) : ?>
<p class="rx-reference-block__back">
<a class="rx-reference-block__link" href="#top">
<?php esc_html_e( 'Back to top', 'rx-theme' ); ?>
</a>
</p>
<?php endif; ?>
<?php endif; ?>
</div>
</section>
Example use in a single post template:
<?php
get_template_part(
'template-parts/blocks/block-reference',
null,
array(
'title' => 'Medical References',
'description' => 'The sources below support the medical information in this article.',
'style' => 'cards',
'columns' => 1,
'search' => true,
'collapsible' => true,
'open' => true,
'references' => array(
array(
'title' => 'Hypertension in adults: diagnosis and management',
'authors' => 'National Institute for Health and Care Excellence',
'source' => 'NICE Guideline',
'year' => '2023',
'url' => 'https://www.nice.org.uk/',
'accessed' => '2026-05-16',
'type' => 'Guideline',
),
array(
'title' => 'Clinical review article title',
'authors' => 'Smith J, Doe A',
'source' => 'Journal of Medicine',
'year' => '2024',
'doi' => '10.1000/example-doi',
'pmid' => '12345678',
'type' => 'Journal',
),
),
)
);
?>
For RX Theme advanced stage, this file is best because it can work now with manual arrays, later with post meta, later with ACF, and later with custom block editor fields.