*/ public function get_defaults() { return array( 'api_key' => '', 'sync_mode' => 'wp_cron', 'sync_frequency' => 30, 'default_author_id' => $this->get_required_author_id(), 'default_category_id' => 0, 'default_post_status' => 'publish', 'cron_token' => wp_generate_password( 32, false, false ), 'last_sync' => 0, 'last_run' => 0, ); } /** * Ensures all default settings exist in the database. */ public function set_defaults() { $current = get_option( self::OPTION_NAME, array() ); update_option( self::OPTION_NAME, wp_parse_args( $current, $this->get_defaults() ), false ); } /** * Returns the saved settings merged with defaults. * * @return array */ public function get_all() { $settings = wp_parse_args( get_option( self::OPTION_NAME, array() ), $this->get_defaults() ); if ( empty( $settings['cron_token'] ) ) { $settings['cron_token'] = wp_generate_password( 32, false, false ); update_option( self::OPTION_NAME, $settings, false ); } if ( empty( $settings['default_author_id'] ) || ! get_user_by( 'id', (int) $settings['default_author_id'] ) ) { $settings['default_author_id'] = $this->get_required_author_id(); update_option( self::OPTION_NAME, $settings, false ); } return $settings; } /** * Reads a single setting. * * @param string $key Setting key. * @param mixed $default Fallback value. * @return mixed */ public function get( $key, $default = null ) { $settings = $this->get_all(); return isset( $settings[ $key ] ) ? $settings[ $key ] : $default; } /** * Merges and stores updated settings. * * @param array $values Partial setting values. */ public function update( array $values ) { $current = $this->get_all(); $updated = array_merge( $current, $values ); update_option( self::OPTION_NAME, $updated, false ); } /** * Stores the latest successful sync timestamp. * * @param int $timestamp Unix timestamp. */ public function update_last_sync( $timestamp ) { $this->update( array( 'last_sync' => absint( $timestamp ), ) ); } /** * Stores the wall-clock time of the most recent sync attempt. * * @param int $timestamp Unix timestamp (typically time()). */ public function update_last_run( $timestamp ) { $this->update( array( 'last_run' => absint( $timestamp ), ) ); } /** * Generates and stores a fresh cron token. * * @return string */ public function regenerate_cron_token() { $token = wp_generate_password( 32, false, false ); $this->update( array( 'cron_token' => $token, ) ); return $token; } /** * Masks an API key, keeping the first and last five characters visible. * * @param string $api_key Saved API key. * @return string */ public function mask_api_key( $api_key ) { $api_key = (string) $api_key; $length = strlen( $api_key ); if ( '' === $api_key ) { return ''; } if ( $length <= 10 ) { return str_repeat( 'X', $length ); } return substr( $api_key, 0, 5 ) . str_repeat( 'X', $length - 10 ) . substr( $api_key, -5 ); } /** * Registers the option with the WordPress Settings API. */ public function register() { register_setting( self::OPTION_GROUP, self::OPTION_NAME, array( $this, 'sanitize' ) ); } /** * Sanitizes submitted admin settings before saving. * * @param array $input Raw submitted values. * @return array */ public function sanitize( $input ) { $current = $this->get_all(); $author_id = isset( $input['default_author_id'] ) ? absint( $input['default_author_id'] ) : $current['default_author_id']; $category_id = isset( $input['default_category_id'] ) ? absint( $input['default_category_id'] ) : $current['default_category_id']; $sync_mode = isset( $input['sync_mode'] ) ? sanitize_key( $input['sync_mode'] ) : $current['sync_mode']; $post_status = isset( $input['default_post_status'] ) ? sanitize_key( $input['default_post_status'] ) : $current['default_post_status']; $api_key = isset( $input['api_key'] ) ? sanitize_text_field( $input['api_key'] ) : ''; $masked_key = $this->mask_api_key( $current['api_key'] ); if ( ! in_array( $sync_mode, array( 'wp_cron', 'manual_cron' ), true ) ) { $sync_mode = 'wp_cron'; } if ( ! in_array( $post_status, array( 'publish', 'draft', 'pending', 'private' ), true ) ) { $post_status = 'publish'; } if ( $author_id <= 0 || ! get_user_by( 'id', $author_id ) ) { $author_id = $this->get_required_author_id(); } if ( $category_id > 0 && ! term_exists( $category_id, 'category' ) ) { $category_id = 0; } return array( 'api_key' => '' !== $api_key && $api_key !== $masked_key ? $api_key : $current['api_key'], 'sync_mode' => $sync_mode, 'sync_frequency' => isset( $input['sync_frequency'] ) ? max( 5, absint( $input['sync_frequency'] ) ) : $current['sync_frequency'], 'default_author_id' => $author_id, 'default_category_id' => $category_id, 'default_post_status' => $post_status, 'cron_token' => isset( $current['cron_token'] ) ? sanitize_text_field( $current['cron_token'] ) : wp_generate_password( 32, false, false ), 'last_sync' => isset( $current['last_sync'] ) ? absint( $current['last_sync'] ) : 0, 'last_run' => isset( $current['last_run'] ) ? absint( $current['last_run'] ) : 0, ); } /** * Returns a valid author ID for imported posts. * * @return int */ private function get_required_author_id() { $current_user_id = get_current_user_id(); if ( $current_user_id > 0 && user_can( $current_user_id, 'edit_posts' ) ) { return $current_user_id; } $users = get_users( array( 'number' => 1, 'orderby' => 'ID', 'order' => 'ASC', 'fields' => array( 'ID' ), 'capability' => 'edit_posts', ) ); if ( ! empty( $users[0]->ID ) ) { return (int) $users[0]->ID; } return 1; } }