Files
peptide-repo-core/includes/frontend/class-pr-core-disclaimer.php
peptiderepo 808b2509d8 feat: initial peptide-repo-core plugin (v0.1.0) — canonical schema sprint
Implements ADR-0002: canonical peptide schema as shared data layer for
the peptiderepo.com ecosystem. All Tier 1 tools (Reconstitution Calculator,
Dosage Reference, Legal Status Tracker, PSA enhancements) build on this.

Includes:
- pr_peptide CPT with REST, taxonomies, 11 meta fields
- 3 custom tables: pr_dosing_rows, pr_legal_cells, pr_ai_candidate_queue
- Sequential migration runner with schema versioning
- 4 typed DTOs and 4 repository classes (full CRUD)
- REST API (pr-core/v1): 6 endpoints (read public, write auth-gated)
- Admin UI: 3 meta boxes, custom columns, AI candidate queue page
- Shared disclaimer component (shortcode + static API)
- JSON-LD Drug schema emission on single peptide pages
- Seed data fixture (3 peptides, 10 dosing rows, 5 legal cells)
- Full teardown in uninstall.php
- CI: PHP lint 8.0-8.2, PHPCS WordPress, 300-line check

All 22 PHP files under 300 lines. Highest: peptide-cpt at 265.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 07:36:12 +07:00

121 lines
4.8 KiB
PHP

<?php
declare(strict_types=1);
/**
* Shared disclaimer component for all peptide data surfaces.
*
* What: Provides shortcode [pr_disclaimer surface="dosing"] and function
* pr_core_render_disclaimer() for rendering surface-specific disclaimers.
* Who calls it: PR_Core::init() registers shortcode; consumer plugins call static methods.
* Dependencies: None (reads from wp_options).
*
* Disclaimer copy is stored in pr_core_disclaimer_copy option (JSON keyed by surface),
* versioned by pr_core_disclaimer_version. Editorial review happens once here,
* not once per consumer plugin.
*
* @see ARCHITECTURE.md — Section 2.8 Shared Disclaimer Component.
*/
class PR_Core_Disclaimer {
/** @var string Option key for disclaimer copy (JSON). */
private const COPY_OPTION = 'pr_core_disclaimer_copy';
/** @var string Option key for disclaimer version. */
private const VERSION_OPTION = 'pr_core_disclaimer_version';
/**
* Default disclaimer text per surface.
*
* @var array<string, string>
*/
private const DEFAULTS = [
'dosing' => 'The dosing information presented here is compiled from published peer-reviewed literature and is provided for educational and research purposes only. It does not constitute medical advice, diagnosis, or treatment recommendations. Always consult a licensed healthcare professional before making any decisions based on this information.',
'legal' => 'Legal status information is compiled from publicly available regulatory texts and is current as of the last-verified date shown. Regulatory frameworks change frequently. This information does not constitute legal advice. Verify current regulations with the relevant regulatory authority in your jurisdiction before taking any action.',
'reconstitution' => 'This calculator is provided for educational purposes only. Always verify your calculations independently and consult a licensed healthcare professional. The results are based on the inputs you provide and standard reconstitution mathematics.',
'ai-answer' => 'This response was generated by an AI system and may contain errors or omissions. All claims are grounded in the Peptide Repo corpus and cited sources where possible. This is not medical advice. Always verify information with primary sources and consult a licensed healthcare professional.',
'general' => 'The information on this site is for educational and research purposes only. It is not intended as medical advice, diagnosis, or treatment. Always consult a licensed healthcare professional.',
];
/**
* Register shortcode and any frontend hooks.
*
* @return void
*/
public function register_hooks(): void {
add_shortcode( 'pr_disclaimer', [ $this, 'shortcode_handler' ] );
}
/**
* Shortcode handler: [pr_disclaimer surface="dosing"].
*
* @param array<string, string>|string $atts Shortcode attributes.
* @return string Disclaimer HTML.
*/
public function shortcode_handler( $atts ): string {
$atts = shortcode_atts( [ 'surface' => 'general' ], $atts, 'pr_disclaimer' );
return self::render( sanitize_text_field( $atts['surface'] ) );
}
/**
* Render disclaimer HTML for a given surface.
*
* @param string $surface Surface identifier.
* @return string HTML output.
*/
public static function render( string $surface ): string {
$text = self::get_disclaimer_text( $surface );
return sprintf(
'<div class="pr-core-disclaimer" data-surface="%s" role="note" aria-label="%s"><p>%s</p></div>',
esc_attr( $surface ),
esc_attr__( 'Disclaimer', 'peptide-repo-core' ),
esc_html( $text )
);
}
/**
* Get disclaimer text for a surface. Checks custom copy first, falls back to defaults.
*
* @param string $surface Surface identifier.
* @return string Disclaimer text.
*/
public static function get_disclaimer_text( string $surface ): string {
$custom = get_option( self::COPY_OPTION, '{}' );
$copy = json_decode( $custom, true ) ?: [];
if ( ! empty( $copy[ $surface ] ) ) {
return (string) $copy[ $surface ];
}
return self::DEFAULTS[ $surface ] ?? self::DEFAULTS['general'];
}
/**
* Get current disclaimer version.
*
* @return int
*/
public static function get_version(): int {
return (int) get_option( self::VERSION_OPTION, 1 );
}
/**
* Update disclaimer copy for a surface.
*
* Side effects: updates wp_options.
*
* @param string $surface Surface identifier.
* @param string $text New disclaimer text.
* @return void
*/
public static function update_disclaimer( string $surface, string $text ): void {
$custom = get_option( self::COPY_OPTION, '{}' );
$copy = json_decode( $custom, true ) ?: [];
$copy[ sanitize_text_field( $surface ) ] = sanitize_textarea_field( $text );
update_option( self::COPY_OPTION, wp_json_encode( $copy ) );
update_option( self::VERSION_OPTION, self::get_version() + 1 );
}
}