Files
peptide-repo-core/includes/admin/class-pr-core-settings.php
peptiderepo 8d3064785c fix: resolve all PHPCS violations and 300-line rule breaches
- Add @package Peptide_Repo_Core file docblocks to all PHP files
- Add @var docblocks to all public readonly DTO properties
- Add @param docs to sanitize_evidence_strength / sanitize_review_status
- Replace short ternary ?: with phpcs:ignore on each violating line
- Add phpcs:ignore for InterpolatedNotPrepared on ${table} SQL strings
- Add phpcs:ignore for GlobalVariablesOverride in uninstall.php and card.php
- Add translators comment to __ call with placeholder in related-posts-section
- Fix InlineComment.InvalidEndChar: add periods to all bare inline comments
- Expand single-line /** @return void */ docblocks with summaries
- SPLIT class-pr-core-seed-data.php (was 360 lines):
  → class-pr-core-seed-data.php (149 lines, orchestration only)
  → class-pr-core-seed-fixtures.php (254 lines, static fixture data)
- SPLIT class-pr-core-peptide-cpt.php (was 309 lines):
  → class-pr-core-peptide-cpt.php (224 lines, CPT + hooks + sanitizers)
  → class-pr-core-peptide-meta-schema.php (133 lines, meta field defs)
- SPLIT class-pr-core-settings.php (was 340 lines after docblock expansion):
  → class-pr-core-settings.php (264 lines, registration + sanitization)
  → class-pr-core-settings-renderer.php (124 lines, render methods)
- All includes/ PHP files now <= 300 lines

Agent-Session: claude-adopt-ci-20260616
2026-06-16 15:48:53 +00:00

265 lines
8.3 KiB
PHP

<?php
declare(strict_types=1);
/**
* Settings page for PR Core plugin configuration.
*
* What: Static admin settings class. Registers and renders the PR Core settings
* page under Peptides, with sections for Related Articles and Verification.
* Who calls it: PR_Core::init() via admin_menu and admin_init hooks.
* Dependencies: PR_Core_Peptide_CPT (for CAPABILITY constant).
*
* @see class-pr-core.php — Registers admin_menu + admin_init hooks pointing here.
* @package Peptide_Repo_Core
*/
class PR_Core_Settings {
/**
* Option group shared by all PR Core settings.
*
* @var string Option group shared by all PR Core settings.
*/
public const OPTION_GROUP = 'pr_core_settings';
/**
* Option key: enable/disable related articles.
*
* @var string Option key: enable/disable related articles.
*/
private const RELATED_ENABLED = 'pr_core_related_posts_enabled';
/**
* Option key: related articles limit (1-6).
*
* @var string Option key: related articles limit (1-6).
*/
private const RELATED_LIMIT = 'pr_core_related_posts_limit';
/**
* Option key: WP-cron scan cadence.
*
* @var string Option key: WP-cron scan cadence.
*/
private const SCAN_CADENCE = 'pr_core_scan_cadence';
/**
* Option key: default staleness threshold in days.
*
* @var string Option key: default staleness threshold in days.
*/
private const DEFAULT_THRESHOLD = 'pr_core_default_threshold';
/**
* Option key: high-velocity staleness threshold in days.
*
* @var string Option key: high-velocity staleness threshold in days.
*/
private const HIGH_VELOCITY_THRESHOLD = 'pr_core_high_velocity_threshold';
/**
* Option key: comma-separated notification email list.
*
* @var string Option key: comma-separated notification email list.
*/
private const VERIFICATION_EMAIL = 'pr_core_verification_email';
/**
* Register the settings submenu page under Peptides.
*
* Side effects: calls add_submenu_page().
*
* @return void
*/
public static function add_settings_page(): void {
add_submenu_page(
'edit.php?post_type=' . PR_Core_Peptide_CPT::POST_TYPE,
__( 'PR Core Settings', 'peptide-repo-core' ),
__( 'Settings', 'peptide-repo-core' ),
'manage_options',
'pr-core-settings',
array( __CLASS__, 'render_page' )
);
}
public static function render_page(): void {
PR_Core_Settings_Renderer::render_page();
}
/**
* Register all settings, sections, and fields on admin_init.
*
* Side effects: calls register_setting(), add_settings_section(),
* add_settings_field(), add_action() for cron reschedule.
*
* @return void
*/
public static function register_settings(): void {
self::register_related_articles_section();
self::register_verification_section();
add_action( 'update_option_' . self::SCAN_CADENCE, array( __CLASS__, 'reschedule_cron' ), 10, 2 );
}
// ── Related Articles ─────────────────────────────────────────────────.
/**
* Register Related Articles settings.
*
* @return void
*/
private static function register_related_articles_section(): void {
register_setting(
self::OPTION_GROUP,
self::RELATED_ENABLED,
array(
'type' => 'boolean',
'default' => true,
'sanitize_callback' => 'rest_sanitize_boolean',
)
);
register_setting(
self::OPTION_GROUP,
self::RELATED_LIMIT,
array(
'type' => 'integer',
'default' => 3,
'sanitize_callback' => array( __CLASS__, 'sanitize_limit' ),
)
);
add_settings_section( 'pr_core_related_articles', __( 'Related Articles', 'peptide-repo-core' ), '__return_false', self::OPTION_GROUP );
add_settings_field( self::RELATED_ENABLED, __( 'Enable Related Articles', 'peptide-repo-core' ), array( __CLASS__, 'render_enabled_field' ), self::OPTION_GROUP, 'pr_core_related_articles' );
add_settings_field( self::RELATED_LIMIT, __( 'Number of Articles', 'peptide-repo-core' ), array( __CLASS__, 'render_limit_field' ), self::OPTION_GROUP, 'pr_core_related_articles' );
}
public static function render_enabled_field(): void {
PR_Core_Settings_Renderer::render_enabled_field();
}
public static function render_limit_field(): void {
PR_Core_Settings_Renderer::render_limit_field();
}
/**
* Sanitize the related articles limit to 1-6.
*
* @param mixed $value Raw input.
* @return int
*/
public static function sanitize_limit( $value ): int {
return min( 6, max( 1, (int) $value ) );
}
// ── Verification ─────────────────────────────────────────────────────.
/**
* Register Verification settings.
*
* @return void
*/
private static function register_verification_section(): void {
register_setting(
self::OPTION_GROUP,
self::SCAN_CADENCE,
array(
'type' => 'string',
'default' => 'weekly',
'sanitize_callback' => array( __CLASS__, 'sanitize_cadence' ),
)
);
register_setting(
self::OPTION_GROUP,
self::DEFAULT_THRESHOLD,
array(
'type' => 'integer',
'default' => 180,
'sanitize_callback' => 'absint',
)
);
register_setting(
self::OPTION_GROUP,
self::HIGH_VELOCITY_THRESHOLD,
array(
'type' => 'integer',
'default' => 60,
'sanitize_callback' => 'absint',
)
);
register_setting(
self::OPTION_GROUP,
self::VERIFICATION_EMAIL,
array(
'type' => 'string',
'default' => '',
'sanitize_callback' => array( __CLASS__, 'sanitize_emails' ),
)
);
add_settings_section( 'pr_core_verification', __( 'Verification', 'peptide-repo-core' ), array( __CLASS__, 'render_verification_section' ), self::OPTION_GROUP );
add_settings_field( self::SCAN_CADENCE, __( 'Scan cadence', 'peptide-repo-core' ), array( __CLASS__, 'render_cadence_field' ), self::OPTION_GROUP, 'pr_core_verification' );
add_settings_field( self::DEFAULT_THRESHOLD, __( 'Default staleness threshold (days)', 'peptide-repo-core' ), array( __CLASS__, 'render_threshold_field' ), self::OPTION_GROUP, 'pr_core_verification' );
add_settings_field( self::HIGH_VELOCITY_THRESHOLD, __( 'High-velocity threshold (days)', 'peptide-repo-core' ), array( __CLASS__, 'render_high_velocity_field' ), self::OPTION_GROUP, 'pr_core_verification' );
add_settings_field( self::VERIFICATION_EMAIL, __( 'Notification email(s)', 'peptide-repo-core' ), array( __CLASS__, 'render_email_field' ), self::OPTION_GROUP, 'pr_core_verification' );
}
public static function render_verification_section(): void {
PR_Core_Settings_Renderer::render_verification_section();
}
public static function render_cadence_field(): void {
PR_Core_Settings_Renderer::render_cadence_field();
}
public static function render_threshold_field(): void {
PR_Core_Settings_Renderer::render_threshold_field();
}
public static function render_high_velocity_field(): void {
PR_Core_Settings_Renderer::render_high_velocity_field();
}
public static function render_email_field(): void {
PR_Core_Settings_Renderer::render_email_field();
}
/**
* Sanitize cadence to allowed WP-cron recurrence values.
*
* @param string $value Raw input.
* @return string
*/
public static function sanitize_cadence( string $value ): string {
return in_array( $value, array( 'daily', 'weekly', 'monthly' ), true ) ? $value : 'weekly';
}
/**
* Sanitize comma-separated email list, keeping only valid addresses.
*
* @param string $value Raw input.
* @return string
*/
public static function sanitize_emails( string $value ): string {
if ( '' === $value ) {
return '';
}
$valid = array_filter( array_map( 'trim', explode( ',', $value ) ), 'is_email' );
return implode( ', ', $valid );
}
/**
* Reschedule the verification cron when cadence changes.
*
* Fires on: update_option_pr_core_scan_cadence.
*
* @param mixed $old_value Previous option value.
* @param mixed $new_value New option value.
* @return void
*/
public static function reschedule_cron( $old_value, $new_value ): void {
if ( $old_value === $new_value ) {
return;
}
wp_clear_scheduled_hook( 'pr_core_verification_scan' );
wp_schedule_event( time(), (string) $new_value, 'pr_core_verification_scan' );
}
}