*/ public static function defaults(): array { $defaults = [ 'color' => '#4F46E5', 'privacy_page_id' => (int) get_option( 'wp_page_for_privacy_policy' ), 'wall_page_id' => 0, 'form_page_id' => 0, 'thanks_text' => __( 'Thank you! Your review has been received and will appear once approved.', 'admirly' ), 'form_title' => __( 'Share your experience', 'admirly' ), 'language_mode' => 'all', 'delete_on_uninstall' => false, 'preset' => '', 'fields' => FieldConfig::DEFAULTS, ]; /** @param array $defaults */ return (array) apply_filters( 'admirly_default_settings', $defaults ); } /** Current options merged over defaults. @return array */ public static function all(): array { $opts = get_option( Plugin::OPTION, [] ); return array_merge( self::defaults(), is_array( $opts ) ? $opts : [] ); } public function register(): void { add_action( 'admin_init', [ $this, 'registerSettings' ] ); add_action( 'admin_menu', [ $this, 'addPage' ] ); } public function registerSettings(): void { register_setting( self::GROUP, Plugin::OPTION, [ 'type' => 'array', 'sanitize_callback' => [ $this, 'sanitize' ], 'default' => [], ] ); } /** * Settings form posts carry a `_tab` key: only that tab's keys change, everything else is preserved. * Programmatic updates (wizard, presets) have no `_tab` and are taken as a whole. * Extensions sanitize their own keys through `admirly_sanitize_settings`. * * @param mixed $in @return array */ public function sanitize( $in ): array { $in = is_array( $in ) ? $in : []; $cur = self::all(); $out = $cur; $tab = isset( $in['_tab'] ) ? sanitize_key( (string) $in['_tab'] ) : ''; if ( $tab === '' ) { $color = sanitize_hex_color( (string) ( $in['color'] ?? $cur['color'] ) ); $out['color'] = $color ? $color : $cur['color']; $out['privacy_page_id'] = max( 0, (int) ( $in['privacy_page_id'] ?? $cur['privacy_page_id'] ) ); $out['wall_page_id'] = max( 0, (int) ( $in['wall_page_id'] ?? $cur['wall_page_id'] ) ); $out['form_page_id'] = max( 0, (int) ( $in['form_page_id'] ?? $cur['form_page_id'] ) ); $out['thanks_text'] = sanitize_textarea_field( (string) ( $in['thanks_text'] ?? $cur['thanks_text'] ) ); $out['form_title'] = sanitize_text_field( (string) ( $in['form_title'] ?? $cur['form_title'] ) ); $out['language_mode'] = ( $in['language_mode'] ?? $cur['language_mode'] ) === 'current' ? 'current' : 'all'; $out['delete_on_uninstall'] = ! empty( $in['delete_on_uninstall'] ?? $cur['delete_on_uninstall'] ); $out['preset'] = sanitize_key( (string) ( $in['preset'] ?? $cur['preset'] ) ); $out['fields'] = FieldConfig::normalize( (array) ( $in['fields'] ?? $cur['fields'] ) ); } elseif ( $tab === 'general' ) { $color = sanitize_hex_color( (string) ( $in['color'] ?? '' ) ); $out['color'] = $color ? $color : self::defaults()['color']; $out['privacy_page_id'] = max( 0, (int) ( $in['privacy_page_id'] ?? 0 ) ); $out['wall_page_id'] = max( 0, (int) ( $in['wall_page_id'] ?? 0 ) ); $out['form_page_id'] = max( 0, (int) ( $in['form_page_id'] ?? 0 ) ); $out['thanks_text'] = sanitize_textarea_field( (string) ( $in['thanks_text'] ?? '' ) ); $out['form_title'] = sanitize_text_field( (string) ( $in['form_title'] ?? '' ) ); $out['language_mode'] = ( $in['language_mode'] ?? 'all' ) === 'current' ? 'current' : 'all'; } elseif ( $tab === 'fields' ) { $f = FieldConfig::normalize( (array) $cur['fields'] ); $preset = sanitize_key( (string) ( $in['apply_preset'] ?? '' ) ); if ( $preset !== '' && Plugin::preset( $preset ) ) { $p = Plugin::preset( $preset ); $out['fields'] = $p['fields']; $out['preset'] = $preset; $out['form_title'] = $p['form_title']; $out['thanks_text'] = $p['thanks_text']; return $out; } $out['fields'] = FieldConfig::normalize( [ 'roles' => FieldConfig::parseList( (string) ( $in['roles_text'] ?? '' ), $f['roles'] ), 'sub' => [ 'enabled' => ! empty( $in['sub_enabled'] ), 'label' => sanitize_text_field( (string) ( $in['sub_label'] ?? '' ) ), 'options' => FieldConfig::parseList( (string) ( $in['sub_options_text'] ?? '' ), $f['sub']['options'] ), 'roles' => array_map( 'sanitize_key', (array) ( $in['sub_roles'] ?? [] ) ), 'required' => ! empty( $in['sub_required'] ), ], ] ); } elseif ( $tab === 'tools' ) { $out['delete_on_uninstall'] = ! empty( $in['delete_on_uninstall'] ); } /** @param array $out Sanitized options so far. @param array $in Raw input. @param array $cur Current options. @param string $tab Settings tab ('' = whole option). */ return (array) apply_filters( 'admirly_sanitize_settings', $out, $in, $cur, $tab ); } public function addPage(): void { add_options_page( 'Admirly', 'Admirly', 'manage_options', self::PAGE, [ $this, 'render' ] ); } public function render(): void { if ( ! current_user_can( 'manage_options' ) ) { return; } $tab = sanitize_key( (string) ( $_GET['tab'] ?? 'general' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only tab switch if ( ! in_array( $tab, self::TABS, true ) ) { $tab = 'general'; } Shortcodes::display( 'admin/settings', [ 'tab' => $tab, 'opts' => self::all(), 'name' => Plugin::OPTION, ] ); } public static function url( string $tab = 'general' ): string { return admin_url( 'options-general.php?page=' . self::PAGE . '&tab=' . $tab ); } }