diff --git a/includes/class-psa-post-type.php b/includes/class-psa-post-type.php index e76a528..04d7bfb 100644 --- a/includes/class-psa-post-type.php +++ b/includes/class-psa-post-type.php @@ -6,57 +6,57 @@ */ if ( ! defined( 'ABSPATH' ) ) { - exit; + exit; } class PSA_Post_Type { - /** Meta field keys used throughout the plugin. */ - const META_FIELDS = array( - 'psa_sequence' => 'Amino Acid Sequence', - 'psa_molecular_weight' => 'Molecular Weight', - 'psa_molecular_formula' => 'Molecular Formula', - 'psa_aliases' => 'Aliases / Synonyms', - 'psa_mechanism' => 'Mechanism of Action', - 'psa_research_apps' => 'Research Applications', - 'psa_safety_profile' => 'Safety & Side Effects', - 'psa_dosage_info' => 'Dosage Information', - 'psa_references' => 'References & Citations', - 'psa_source' => 'Data Source', - 'psa_pubchem_cid' => 'PubChem CID', - ); + /** Meta field keys used throughout the plugin. */ + const META_FIELDS = array( + 'psa_sequence' => 'Amino Acid Sequence', + 'psa_molecular_weight' => 'Molecular Weight', + 'psa_molecular_formula' => 'Molecular Formula', + 'psa_aliases' => 'Aliases / Synonyms', + 'psa_mechanism' => 'Mechanism of Action', + 'psa_research_apps' => 'Research Applications', + 'psa_safety_profile' => 'Safety & Side Effects', + 'psa_dosage_info' => 'Dosage Information', + 'psa_references' => 'References & Citations', + 'psa_source' => 'Data Source', + 'psa_pubchem_cid' => 'PubChem CID', + ); - /** - * Register admin-only hooks (meta boxes, save). - */ - public static function init_admin() { - add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_boxes' ) ); - add_action( 'save_post_peptide', array( __CLASS__, 'save_meta' ) ); - } + /** + * Register admin-only hooks (meta boxes, save). + */ + public static function init_admin() { + add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_boxes' ) ); + add_action( 'save_post_peptide', array( __CLASS__, 'save_meta' ) ); + } - /** - * Register the peptide CPT. - */ - public static function register_peptide_post_type() { - if ( post_type_exists( 'peptide' ) ) { - return; - } + /** + * Register the peptide CPT. + */ + public static function register_peptide_post_type() { + if ( post_type_exists( 'peptide' ) ) { + return; + } - $labels = array( - 'name' => 'Peptides', - 'singular_name' => 'Peptide', - 'menu_name' => 'Peptides', - 'add_new' => 'Add Peptide', - 'add_new_item' => 'Add New Peptide', - 'edit_item' => 'Edit Peptide', - 'new_item' => 'New Peptide', - 'view_item' => 'View Peptide', - 'search_items' => 'Search Peptides', - 'not_found' => 'No peptides found', - 'not_found_in_trash' => 'No peptides found in Trash', - ); + $labels = array( + 'name' => 'Peptides', + 'singular_name' => 'Peptide', + 'menu_name' => 'Peptides', + 'add_new' => 'Add Peptide', + 'add_new_item' => 'Add New Peptide', + 'edit_item' => 'Edit Peptide', + 'new_item' => 'New Peptide', + 'view_item' => 'View Peptide', + 'search_items' => 'Search Peptides', + 'not_found' => 'No peptides found', + 'not_found_in_trash' => 'No peptides found in Trash', + ); - register_post_type( + register_post_type( 'peptide', array( 'labels' => $labels, @@ -70,89 +70,89 @@ class PSA_Post_Type { 'hierarchical' => false, ) ); - } + } - /** - * Add meta boxes to the peptide editor. - */ - public static function add_meta_boxes() { - add_meta_box( - 'psa_peptide_data', - 'Peptide Scientific Data', - array( __CLASS__, 'render_meta_box' ), - 'peptide', - 'normal', - 'high' - ); - } + /** + * Add meta boxes to the peptide editor. + */ + public static function add_meta_boxes() { + add_meta_box( + 'psa_peptide_data', + 'Peptide Scientific Data', + array( __CLASS__, 'render_meta_box' ), + 'peptide', + 'normal', + 'high' + ); + } - /** - * Render the meta box fields. - * - * @param WP_Post $post The current post object. - */ - public static function render_meta_box( $post ) { - wp_nonce_field( 'psa_save_meta', 'psa_meta_nonce' ); + /** + * Render the meta box fields. + * + * @param WP_Post $post The current post object. + */ + public static function render_meta_box( $post ) { + wp_nonce_field( 'psa_save_meta', 'psa_meta_nonce' ); - $textarea_fields = array( - 'psa_sequence', 'psa_mechanism', 'psa_research_apps', - 'psa_safety_profile', 'psa_dosage_info', 'psa_references', - ); + $textarea_fields = array( + 'psa_sequence', 'psa_mechanism', 'psa_research_apps', + 'psa_safety_profile', 'psa_dosage_info', 'psa_references', + ); - echo ''; - foreach ( self::META_FIELDS as $key => $label ) { - $value = get_post_meta( $post->ID, $key, true ); - echo ''; - echo ''; - echo ''; + } + echo '
'; + echo ''; + foreach ( self::META_FIELDS as $key => $label ) { + $value = get_post_meta( $post->ID, $key, true ); + echo ''; + echo ''; + echo ''; - } - echo '
'; - if ( $key === 'psa_source' ) { - $options = array( - '' => '— Select —', - 'ai-generated' => 'AI Generated', - 'pubchem' => 'PubChem Verified', - 'manual' => 'Manual Entry', - 'pending' => 'Pending Generation', - ); - echo ''; - } elseif ( in_array( $key, $textarea_fields, true ) ) { - echo ''; - } else { - echo ''; - } + if ( $key === 'psa_source' ) { + $options = array( + '' => '— Select —', + 'ai-generated' => 'AI Generated', + 'pubchem' => 'PubChem Verified', + 'manual' => 'Manual Entry', + 'pending' => 'Pending Generation', + ); + echo ''; + } elseif ( in_array( $key, $textarea_fields, true ) ) { + echo ''; + } else { + echo ''; + } - echo '
'; - } + echo '
'; + } - /** - * Save meta field values. - * - * @param int $post_id The post ID. - */ - public static function save_meta( $post_id ) { - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput - if ( ! isset( $_POST['psa_meta_nonce'] ) || - ! wp_verify_nonce( wp_unslash( $_POST['psa_meta_nonce'] ), 'psa_save_meta' ) ) { - return; - } - if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { - return; - } - if ( ! current_user_can( 'edit_post', $post_id ) ) { - return; - } + /** + * Save meta field values. + * + * @param int $post_id The post ID. + */ + public static function save_meta( $post_id ) { + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput + if ( ! isset( $_POST['psa_meta_nonce'] ) || + ! wp_verify_nonce( wp_unslash( $_POST['psa_meta_nonce'] ), 'psa_save_meta' ) ) { + return; + } + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { + return; + } + if ( ! current_user_can( 'edit_post', $post_id ) ) { + return; + } - foreach ( self::META_FIELDS as $key => $label ) { - if ( isset( $_POST[ $key ] ) ) { - $value = sanitize_textarea_field( wp_unslash( $_POST[ $key ] ) ); - update_post_meta( $post_id, $key, $value ); - } - } - } + foreach ( self::META_FIELDS as $key => $label ) { + if ( isset( $_POST[ $key ] ) ) { + $value = sanitize_textarea_field( wp_unslash( $_POST[ $key ] ) ); + update_post_meta( $post_id, $key, $value ); + } + } + } } diff --git a/includes/class-psa-pubchem.php b/includes/class-psa-pubchem.php index 9f82c6a..b42e15e 100644 --- a/includes/class-psa-pubchem.php +++ b/includes/class-psa-pubchem.php @@ -6,82 +6,82 @@ */ if ( ! defined( 'ABSPATH' ) ) { - exit; + exit; } class PSA_PubChem { - const API_BASE = 'https://pubchem.ncbi.nlm.nih.gov/rest/pug'; + const API_BASE = 'https://pubchem.ncbi.nlm.nih.gov/rest/pug'; - /** - * Look up a peptide/compound by name and return molecular properties. - * Results are cached in transients to respect PubChem rate limits. - * - * @param string $name Peptide or compound name. - * @return array|WP_Error|null Array of properties, WP_Error on failure, or null if not found. - */ - public static function lookup( $name ) { - $options = get_option( 'psa_settings', array() ); - $use_pubchem = $options['use_pubchem'] ?? '1'; + /** + * Look up a peptide/compound by name and return molecular properties. + * Results are cached in transients to respect PubChem rate limits. + * + * @param string $name Peptide or compound name. + * @return array|WP_Error|null Array of properties, WP_Error on failure, or null if not found. + */ + public static function lookup( $name ) { + $options = get_option( 'psa_settings', array() ); + $use_pubchem = $options['use_pubchem'] ?? '1'; - if ( '1' !== $use_pubchem ) { - return null; - } + if ( '1' !== $use_pubchem ) { + return null; + } - // Check cache first (respect PubChem's 5 req/sec limit). - $cache_key = 'psa_pubchem_' . md5( strtolower( trim( $name ) ) ); - $cached = get_transient( $cache_key ); - if ( false !== $cached ) { - // We store 'not_found' string to differentiate from cache miss. - if ( 'not_found' === $cached ) { - return null; - } - if ( is_wp_error( $cached ) ) { - return $cached; - } - return $cached; - } + // Check cache first (respect PubChem's 5 req/sec limit). + $cache_key = 'psa_pubchem_' . md5( strtolower( trim( $name ) ) ); + $cached = get_transient( $cache_key ); + if ( false !== $cached ) { + // We store 'not_found' string to differentiate from cache miss. + if ( 'not_found' === $cached ) { + return null; + } + if ( is_wp_error( $cached ) ) { + return $cached; + } + return $cached; + } - // Step 1: Get the CID by compound name. - $cid = self::get_cid_by_name( $name ); - if ( is_wp_error( $cid ) ) { - set_transient( $cache_key, $cid, 1 * HOUR_IN_SECONDS ); - return $cid; - } - if ( ! $cid ) { - set_transient( $cache_key, 'not_found', 12 * HOUR_IN_SECONDS ); - return null; - } + // Step 1: Get the CID by compound name. + $cid = self::get_cid_by_name( $name ); + if ( is_wp_error( $cid ) ) { + set_transient( $cache_key, $cid, 1 * HOUR_IN_SECONDS ); + return $cid; + } + if ( ! $cid ) { + set_transient( $cache_key, 'not_found', 12 * HOUR_IN_SECONDS ); + return null; + } - // Step 2: Get properties by CID. - $properties = self::get_properties( $cid ); - if ( is_wp_error( $properties ) ) { - set_transient( $cache_key, $properties, 1 * HOUR_IN_SECONDS ); - return $properties; - } - if ( ! $properties ) { - set_transient( $cache_key, 'not_found', 12 * HOUR_IN_SECONDS ); - return null; - } + // Step 2: Get properties by CID. + $properties = self::get_properties( $cid ); + if ( is_wp_error( $properties ) ) { + set_transient( $cache_key, $properties, 1 * HOUR_IN_SECONDS ); + return $properties; + } + if ( ! $properties ) { + set_transient( $cache_key, 'not_found', 12 * HOUR_IN_SECONDS ); + return null; + } - $properties['cid'] = $cid; + $properties['cid'] = $cid; - // Cache successful lookups for 7 days. - set_transient( $cache_key, $properties, PSA_Config::PUBCHEM_CACHE_TTL ?: 7 * DAY_IN_SECONDS ); + // Cache successful lookups for 7 days. + set_transient( $cache_key, $properties, PSA_Config::PUBCHEM_CACHE_TTL ?: 7 * DAY_IN_SECONDS ); - return $properties; - } + return $properties; + } - /** - * Get PubChem Compound ID (CID) by name. - * - * @param string $name Compound name. - * @return int|WP_Error|null CID, WP_Error on API failure, or null if not found. - */ - private static function get_cid_by_name( $name ) { - $url = self::API_BASE . '/compound/name/' . rawurlencode( $name ) . '/cids/JSON'; + /** + * Get PubChem Compound ID (CID) by name. + * + * @param string $name Compound name. + * @return int|WP_Error|null CID, WP_Error on API failure, or null if not found. + */ + private static function get_cid_by_name( $name ) { + $url = self::API_BASE . '/compound/name/' . rawurlencode( $name ) . '/cids/JSON'; - $response = wp_remote_get( + $response = wp_remote_get( $url, array( 'timeout' => 15, @@ -89,33 +89,33 @@ class PSA_PubChem { ) ); - if ( is_wp_error( $response ) ) { - return new WP_Error( 'pubchem_api_error', 'PubChem API request failed: ' . $response->get_error_message() ); - } + if ( is_wp_error( $response ) ) { + return new WP_Error( 'pubchem_api_error', 'PubChem API request failed: ' . $response->get_error_message() ); + } - $code = wp_remote_retrieve_response_code( $response ); - if ( 200 !== $code ) { - return new WP_Error( 'pubchem_http_error', 'PubChem returned HTTP ' . $code ); - } + $code = wp_remote_retrieve_response_code( $response ); + if ( 200 !== $code ) { + return new WP_Error( 'pubchem_http_error', 'PubChem returned HTTP ' . $code ); + } - $body = json_decode( wp_remote_retrieve_body( $response ), true ); + $body = json_decode( wp_remote_retrieve_body( $response ), true ); - return isset( $body['IdentifierList']['CID'][0] ) - ? (int) $body['IdentifierList']['CID'][0] - : null; - } + return isset( $body['IdentifierList']['CID'][0] ) + ? (int) $body['IdentifierList']['CID'][0] + : null; + } - /** - * Get molecular properties for a given CID. - * - * @param int $cid PubChem Compound ID. - * @return array|WP_Error|null Properties array, WP_Error on failure, or null if not found. - */ - private static function get_properties( $cid ) { - $props = 'MolecularFormula,MolecularWeight,IUPACName,CanonicalSMILES,InChI'; - $url = self::API_BASE . '/compound/cid/' . (int) $cid . '/property/' . $props . '/JSON'; + /** + * Get molecular properties for a given CID. + * + * @param int $cid PubChem Compound ID. + * @return array|WP_Error|null Properties array, WP_Error on failure, or null if not found. + */ + private static function get_properties( $cid ) { + $props = 'MolecularFormula,MolecularWeight,IUPACName,CanonicalSMILES,InChI'; + $url = self::API_BASE . '/compound/cid/' . (int) $cid . '/property/' . $props . '/JSON'; - $response = wp_remote_get( + $response = wp_remote_get( $url, array( 'timeout' => 15, @@ -123,29 +123,29 @@ class PSA_PubChem { ) ); - if ( is_wp_error( $response ) ) { - return new WP_Error( 'pubchem_api_error', 'PubChem API request failed: ' . $response->get_error_message() ); - } + if ( is_wp_error( $response ) ) { + return new WP_Error( 'pubchem_api_error', 'PubChem API request failed: ' . $response->get_error_message() ); + } - $code = wp_remote_retrieve_response_code( $response ); - if ( 200 !== $code ) { - return new WP_Error( 'pubchem_http_error', 'PubChem returned HTTP ' . $code ); - } + $code = wp_remote_retrieve_response_code( $response ); + if ( 200 !== $code ) { + return new WP_Error( 'pubchem_http_error', 'PubChem returned HTTP ' . $code ); + } - $body = json_decode( wp_remote_retrieve_body( $response ), true ); + $body = json_decode( wp_remote_retrieve_body( $response ), true ); - if ( ! isset( $body['PropertyTable']['Properties'][0] ) ) { - return null; - } + if ( ! isset( $body['PropertyTable']['Properties'][0] ) ) { + return null; + } - $p = $body['PropertyTable']['Properties'][0]; + $p = $body['PropertyTable']['Properties'][0]; - return array( - 'molecular_formula' => $p['MolecularFormula'] ?? '', - 'molecular_weight' => isset( $p['MolecularWeight'] ) ? $p['MolecularWeight'] . ' Da' : '', - 'iupac_name' => $p['IUPACName'] ?? '', - 'smiles' => $p['CanonicalSMILES'] ?? '', - 'inchi' => $p['InChI'] ?? '', - ); - } + return array( + 'molecular_formula' => $p['MolecularFormula'] ?? '', + 'molecular_weight' => isset( $p['MolecularWeight'] ) ? $p['MolecularWeight'] . ' Da' : '', + 'iupac_name' => $p['IUPACName'] ?? '', + 'smiles' => $p['CanonicalSMILES'] ?? '', + 'inchi' => $p['InChI'] ?? '', + ); + } } diff --git a/includes/class-psa-template.php b/includes/class-psa-template.php index 793cf15..603918b 100644 --- a/includes/class-psa-template.php +++ b/includes/class-psa-template.php @@ -6,117 +6,117 @@ */ if ( ! defined( 'ABSPATH' ) ) { - exit; + exit; } class PSA_Template { - public static function init() { - add_filter( 'the_content', array( __CLASS__, 'append_peptide_data' ) ); - } + public static function init() { + add_filter( 'the_content', array( __CLASS__, 'append_peptide_data' ) ); + } - /** - * Append structured peptide data below the post content on single peptide pages. - * - * @param string $content The post content. - * @return string Modified content. - */ - public static function append_peptide_data( $content ) { - if ( ! is_singular( 'peptide' ) || ! in_the_loop() || ! is_main_query() ) { - return $content; - } + /** + * Append structured peptide data below the post content on single peptide pages. + * + * @param string $content The post content. + * @return string Modified content. + */ + public static function append_peptide_data( $content ) { + if ( ! is_singular( 'peptide' ) || ! in_the_loop() || ! is_main_query() ) { + return $content; + } - $post_id = get_the_ID(); - $source = get_post_meta( $post_id, 'psa_source', true ); - $pubchem_cid = get_post_meta( $post_id, 'psa_pubchem_cid', true ); + $post_id = get_the_ID(); + $source = get_post_meta( $post_id, 'psa_source', true ); + $pubchem_cid = get_post_meta( $post_id, 'psa_pubchem_cid', true ); - // Build template data for filtering. - $data = array( - 'post_id' => $post_id, - 'source' => $source, - 'pubchem_cid' => $pubchem_cid, - ); + // Build template data for filtering. + $data = array( + 'post_id' => $post_id, + 'source' => $source, + 'pubchem_cid' => $pubchem_cid, + ); - // Allow filtering of template data before rendering. - $data = apply_filters( 'psa_template_data', $data, $post_id ); + // Allow filtering of template data before rendering. + $data = apply_filters( 'psa_template_data', $data, $post_id ); - // Restore values from possibly filtered data. - $post_id = $data['post_id'] ?? $post_id; - $source = $data['source'] ?? $source; - $pubchem_cid = $data['pubchem_cid'] ?? $pubchem_cid; + // Restore values from possibly filtered data. + $post_id = $data['post_id'] ?? $post_id; + $source = $data['source'] ?? $source; + $pubchem_cid = $data['pubchem_cid'] ?? $pubchem_cid; - $html = '
'; + $html = '
'; - // Quick facts box. - $weight = get_post_meta( $post_id, 'psa_molecular_weight', true ); - $formula = get_post_meta( $post_id, 'psa_molecular_formula', true ); - $seq = get_post_meta( $post_id, 'psa_sequence', true ); - $aliases = get_post_meta( $post_id, 'psa_aliases', true ); + // Quick facts box. + $weight = get_post_meta( $post_id, 'psa_molecular_weight', true ); + $formula = get_post_meta( $post_id, 'psa_molecular_formula', true ); + $seq = get_post_meta( $post_id, 'psa_sequence', true ); + $aliases = get_post_meta( $post_id, 'psa_aliases', true ); - if ( $weight || $formula || $seq ) { - $html .= '
'; - $html .= '

Quick Facts

'; - $html .= ''; + if ( $weight || $formula || $seq ) { + $html .= '
'; + $html .= '

Quick Facts

'; + $html .= '
'; - if ( $aliases ) { - $html .= ''; - } - if ( $seq ) { - $html .= ''; - } - if ( $formula ) { - $html .= ''; - } - if ( $weight ) { - $html .= ''; - } - if ( $pubchem_cid ) { - $cid_int = intval( $pubchem_cid ); - $html .= ''; - } + if ( $aliases ) { + $html .= ''; + } + if ( $seq ) { + $html .= ''; + } + if ( $formula ) { + $html .= ''; + } + if ( $weight ) { + $html .= ''; + } + if ( $pubchem_cid ) { + $cid_int = intval( $pubchem_cid ); + $html .= ''; + } - $html .= '
Also Known As' . esc_html( $aliases ) . '
Sequence' . esc_html( $seq ) . '
Molecular Formula' . esc_html( $formula ) . '
Molecular Weight' . esc_html( $weight ) . '
PubChem CID' . $cid_int . '
Also Known As' . esc_html( $aliases ) . '
Sequence' . esc_html( $seq ) . '
Molecular Formula' . esc_html( $formula ) . '
Molecular Weight' . esc_html( $weight ) . '
PubChem CID' . $cid_int . '
'; - $html .= '
'; - } + $html .= ''; + $html .= '
'; + } - // Detailed sections. - $detail_fields = array( - 'psa_mechanism' => 'Mechanism of Action', - 'psa_research_apps' => 'Research Applications', - 'psa_safety_profile' => 'Safety & Side Effects', - 'psa_dosage_info' => 'Dosage Information', - 'psa_references' => 'References', - ); + // Detailed sections. + $detail_fields = array( + 'psa_mechanism' => 'Mechanism of Action', + 'psa_research_apps' => 'Research Applications', + 'psa_safety_profile' => 'Safety & Side Effects', + 'psa_dosage_info' => 'Dosage Information', + 'psa_references' => 'References', + ); - foreach ( $detail_fields as $key => $label ) { - $value = get_post_meta( $post_id, $key, true ); - if ( ! empty( $value ) ) { - $html .= '
'; - $html .= '

' . esc_html( $label ) . '

'; - $html .= '
' . wp_kses_post( wpautop( $value ) ) . '
'; - $html .= '
'; - } - } + foreach ( $detail_fields as $key => $label ) { + $value = get_post_meta( $post_id, $key, true ); + if ( ! empty( $value ) ) { + $html .= '
'; + $html .= '

' . esc_html( $label ) . '

'; + $html .= '
' . wp_kses_post( wpautop( $value ) ) . '
'; + $html .= '
'; + } + } - // Source badge. - if ( $source && 'pending' !== $source ) { - $badges = array( - 'ai-generated' => array( 'label' => 'AI-Generated Content', 'class' => 'psa-badge-ai' ), - 'pubchem' => array( 'label' => 'PubChem Verified', 'class' => 'psa-badge-verified' ), - 'manual' => array( 'label' => 'Manually Curated', 'class' => 'psa-badge-manual' ), - ); - $badge = $badges[ $source ] ?? $badges['ai-generated']; + // Source badge. + if ( $source && 'pending' !== $source ) { + $badges = array( + 'ai-generated' => array( 'label' => 'AI-Generated Content', 'class' => 'psa-badge-ai' ), + 'pubchem' => array( 'label' => 'PubChem Verified', 'class' => 'psa-badge-verified' ), + 'manual' => array( 'label' => 'Manually Curated', 'class' => 'psa-badge-manual' ), + ); + $badge = $badges[ $source ] ?? $badges['ai-generated']; - $html .= '
'; - $html .= esc_html( $badge['label'] ); - if ( 'ai-generated' === $source ) { - $html .= ' — This entry was automatically generated and may contain inaccuracies. Please verify critical information with primary sources.'; - } - $html .= '
'; - } + $html .= '
'; + $html .= esc_html( $badge['label'] ); + if ( 'ai-generated' === $source ) { + $html .= ' — This entry was automatically generated and may contain inaccuracies. Please verify critical information with primary sources.'; + } + $html .= '
'; + } - $html .= '
'; + $html .= ''; - return $content . $html; - } + return $content . $html; + } }