array( 'post', 'page' ), 'enable_schema' => 0, 'minify' => 0, 'acf_include' => 1, 'cron_enabled' => 0, 'cron_frequency' => 'daily', 'ping_enabled' => 1, 'ping_urls' => "# Beispiele (Platzhalter anpassen)\nhttps://webhook.site/your-uuid\n", 'ping_events' => array( 'publish', 'update' ), 'ping_secret' => '', 'ping_timeout' => 5, 'ping_retry' => 1, 'openai_prompt' => 'Analysiere den folgenden WordPress Beitrag. Antworte ausschließlich als JSON mit overall_score, issues[], suggested_headings[], faq[].', 'score_preset' => 'standard', 'score_weight_content' => 35, 'score_weight_eeat' => 30, 'score_weight_technical' => 20, 'score_weight_linking' => 15, 'seo_plugin' => 'auto', 'remove_category_base' => 0, 'gtm_id' => '', 'ga4_id' => '', 'gsc_verify' => '', 'meta_pixel' => '', 'gsc_client_id' => '', 'gsc_client_secret' => '', ); } private function get_opts() { $o = get_option( self::OPT_KEY, array() ); if ( ! is_array( $o ) ) { $o = array(); } $defaults = self::defaults(); foreach ( $defaults as $k => $v ) { if ( ! isset( $o[ $k ] ) ) { $o[ $k ] = $v; } } $o['enabled_post_types'] = array_values( array_filter( array_map( 'sanitize_key', (array) $o['enabled_post_types'] ) ) ); $o['enable_schema'] = ! empty( $o['enable_schema'] ) ? 1 : 0; $o['minify'] = ! empty( $o['minify'] ) ? 1 : 0; $o['acf_include'] = ! empty( $o['acf_include'] ) ? 1 : 0; $o['cron_enabled'] = ! empty( $o['cron_enabled'] ) ? 1 : 0; $o['cron_frequency'] = in_array( $o['cron_frequency'], array( 'daily', 'weekly' ), true ) ? $o['cron_frequency'] : 'daily'; $o['ping_enabled'] = ! empty( $o['ping_enabled'] ) ? 1 : 0; $o['ping_urls'] = is_string( $o['ping_urls'] ) ? $o['ping_urls'] : ''; $o['ping_secret'] = is_string( $o['ping_secret'] ) ? $o['ping_secret'] : ''; $o['ping_timeout'] = max( 2, (int) $o['ping_timeout'] ); $o['ping_retry'] = ! empty( $o['ping_retry'] ) ? 1 : 0; if ( ! isset( $o['openai_api_key'] ) ) { $o['openai_api_key'] = ''; } $valid_presets = array( 'ymyl', 'standard', 'ecommerce', 'custom' ); $o['score_preset'] = in_array( $o['score_preset'] ?? 'standard', $valid_presets, true ) ? $o['score_preset'] : 'standard'; $o['score_weight_content'] = max( 5, (int) ( $o['score_weight_content'] ?? 35 ) ); $o['score_weight_eeat'] = max( 5, (int) ( $o['score_weight_eeat'] ?? 30 ) ); $o['score_weight_technical'] = max( 5, (int) ( $o['score_weight_technical'] ?? 20 ) ); $o['score_weight_linking'] = max( 5, (int) ( $o['score_weight_linking'] ?? 15 ) ); $valid_seo = array( 'auto', 'yoast', 'rankmath', 'aioseo', 'own' ); $o['seo_plugin'] = in_array( $o['seo_plugin'] ?? 'auto', $valid_seo, true ) ? $o['seo_plugin'] : 'auto'; return $o; } public function get_opts_public() { return $this->get_opts(); } public static function get_score_weights( $o = array() ) { $presets = array( 'ymyl' => array( 'content' => 30, 'eeat' => 40, 'technical' => 20, 'linking' => 10 ), 'standard' => array( 'content' => 35, 'eeat' => 30, 'technical' => 20, 'linking' => 15 ), 'ecommerce' => array( 'content' => 30, 'eeat' => 20, 'technical' => 35, 'linking' => 15 ), ); $preset = isset( $o['score_preset'] ) ? $o['score_preset'] : 'standard'; if ( $preset !== 'custom' ) { return $presets[ $preset ] ?? $presets['standard']; } $w = array( 'content' => max( 5, (int) ( $o['score_weight_content'] ?? 35 ) ), 'eeat' => max( 5, (int) ( $o['score_weight_eeat'] ?? 30 ) ), 'technical' => max( 5, (int) ( $o['score_weight_technical'] ?? 20 ) ), 'linking' => max( 5, (int) ( $o['score_weight_linking'] ?? 15 ) ), ); $sum = array_sum( $w ); if ( $sum !== 100 ) { foreach ( $w as $k => $v ) { $w[ $k ] = (int) round( $v * 100 / $sum ); } $w['content'] += ( 100 - array_sum( $w ) ); } return $w; } public function get_active_seo_plugin() { $o = $this->get_opts(); $choice = $o['seo_plugin'] ?? 'auto'; if ( $choice !== 'auto' ) { return $choice; } if ( defined( 'WPSEO_VERSION' ) ) { return 'yoast'; } if ( defined( 'RANK_MATH_VERSION' ) ) { return 'rankmath'; } if ( defined( 'AIOSEO_VERSION' ) ) { return 'aioseo'; } return 'own'; } public function register_settings() { register_setting( self::OPT_KEY, self::OPT_KEY, array( $this, 'sanitize_settings' ) ); register_setting( 'aije_ai_settings', 'aije_openai_api_key' ); } public function sanitize_settings( $in ) { $d = self::defaults(); $o = array(); $o['enabled_post_types'] = isset( $in['enabled_post_types'] ) ? array_values( array_map( 'sanitize_key', (array) $in['enabled_post_types'] ) ) : $d['enabled_post_types']; $o['enable_schema'] = ! empty( $in['enable_schema'] ) ? 1 : 0; $o['minify'] = ! empty( $in['minify'] ) ? 1 : 0; $o['acf_include'] = ! empty( $in['acf_include'] ) ? 1 : 0; $o['cron_enabled'] = ! empty( $in['cron_enabled'] ) ? 1 : 0; $o['cron_frequency'] = isset( $in['cron_frequency'] ) ? sanitize_key( $in['cron_frequency'] ) : 'daily'; $o['ping_enabled'] = ! empty( $in['ping_enabled'] ) ? 1 : 0; $o['ping_urls'] = isset( $in['ping_urls'] ) ? (string) $in['ping_urls'] : $d['ping_urls']; $events = array(); if ( ! empty( $in['ping_events'] ) && is_array( $in['ping_events'] ) ) { foreach ( $in['ping_events'] as $e ) { $e = sanitize_key( $e ); if ( in_array( $e, array( 'publish', 'update' ), true ) ) { $events[] = $e; } } } $o['ping_events'] = array_values( array_unique( $events ) ); $o['ping_secret'] = isset( $in['ping_secret'] ) ? (string) $in['ping_secret'] : ''; $o['ping_timeout'] = isset( $in['ping_timeout'] ) ? max( 2, (int) $in['ping_timeout'] ) : 5; $o['ping_retry'] = ! empty( $in['ping_retry'] ) ? 1 : 0; $o['openai_api_key'] = isset( $in['openai_api_key'] ) ? sanitize_text_field( $in['openai_api_key'] ) : ''; $o['openai_prompt'] = isset( $in['openai_prompt'] ) ? trim( $in['openai_prompt'] ) : $d['openai_prompt']; $valid_presets = array( 'ymyl', 'standard', 'ecommerce', 'custom' ); $o['score_preset'] = ( isset( $in['score_preset'] ) && in_array( $in['score_preset'], $valid_presets, true ) ) ? $in['score_preset'] : 'standard'; $o['score_weight_content'] = isset( $in['score_weight_content'] ) ? max( 5, (int) $in['score_weight_content'] ) : 35; $o['score_weight_eeat'] = isset( $in['score_weight_eeat'] ) ? max( 5, (int) $in['score_weight_eeat'] ) : 30; $o['score_weight_technical'] = isset( $in['score_weight_technical'] ) ? max( 5, (int) $in['score_weight_technical'] ) : 20; $o['score_weight_linking'] = isset( $in['score_weight_linking'] ) ? max( 5, (int) $in['score_weight_linking'] ) : 15; $valid_seo = array( 'auto', 'yoast', 'rankmath', 'aioseo', 'own' ); $old_opts = get_option( self::OPT_KEY, array() ); $old_seo = is_array( $old_opts ) && isset( $old_opts['seo_plugin'] ) ? $old_opts['seo_plugin'] : 'auto'; $o['seo_plugin'] = ( isset( $in['seo_plugin'] ) && in_array( $in['seo_plugin'], $valid_seo, true ) ) ? $in['seo_plugin'] : $old_seo; $o['remove_category_base'] = ! empty( $in['remove_category_base'] ) ? 1 : 0; $this->maybe_reschedule_export( $o['cron_enabled'], $o['cron_frequency'] ); $this->ensure_ping_cron(); $o['gtm_id'] = sanitize_text_field( $in['gtm_id'] ?? '' ); $o['ga4_id'] = sanitize_text_field( $in['ga4_id'] ?? '' ); $o['gsc_verify'] = sanitize_text_field( $in['gsc_verify'] ?? '' ); $o['meta_pixel'] = sanitize_text_field( $in['meta_pixel'] ?? '' ); $o['gsc_client_id'] = sanitize_text_field( $in['gsc_client_id'] ?? '' ); $o['gsc_client_secret'] = sanitize_text_field( $in['gsc_client_secret'] ?? '' ); return $o; } /* ===== Lizenz / Trial ===== */ public static function trial_is_active() { $ts = (int) get_option( 'aije_activated_at', 0 ); if ( ! $ts ) { return true; } return ( time() <= $ts + self::TRIAL_DAYS * DAY_IN_SECONDS ); } public static function trial_end_timestamp() { $ts = (int) get_option( 'aije_activated_at', 0 ); if ( ! $ts ) { return 0; } return $ts + self::TRIAL_DAYS * DAY_IN_SECONDS; } public static function is_pro_active() { return true; } private static function get_normalized_host() { $home = function_exists( 'home_url' ) ? home_url() : ''; $site = function_exists( 'site_url' ) ? site_url() : ''; $url = $home ? $home : $site; $host = ''; if ( $url ) { $parsed = @parse_url( $url ); if ( is_array( $parsed ) && ! empty( $parsed['host'] ) ) { $host = strtolower( $parsed['host'] ); } } if ( $host !== '' && strpos( $host, 'www.' ) === 0 ) { $host = substr( $host, 4 ); } return $host; } private static function base_domain( $host ) { if ( ! $host ) { return ''; } $parts = explode( '.', $host ); $n = count( $parts ); if ( $n >= 2 ) { return $parts[ $n - 2 ] . '.' . $parts[ $n - 1 ]; } return $host; } private static function code_from_domain( $domain ) { $domain = strtolower( (string) $domain ); if ( $domain === '' ) { return ''; } return strtoupper( substr( hash( 'sha256', $domain ), 0, 16 ) ); } public static function license_is_valid( $code = null ) { $entered = strtoupper( trim( (string) ( $code ? $code : get_option( self::OPT_LICENSE, '' ) ) ) ); if ( $entered === '' ) { return false; } $host = self::get_normalized_host(); if ( $host === '' ) { return false; } $code1 = self::code_from_domain( $host ); if ( $code1 && hash_equals( $code1, $entered ) ) { return true; } $base = self::base_domain( $host ); if ( $base && $base !== $host ) { $code2 = self::code_from_domain( $base ); if ( $code2 && hash_equals( $code2, $entered ) ) { return true; } } return false; } public static function validate_license_remote( $license_key ) { if ( empty( $license_key ) ) { return array( 'valid' => false, 'error' => 'Kein Lizenzcode angegeben.' ); } $host = self::get_normalized_host(); $response = wp_remote_post( 'https://www.muench-media.com/aije-license/validate', array( 'timeout' => 10, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( array( 'license_key' => $license_key, 'domain' => $host ) ), ) ); if ( is_wp_error( $response ) ) { return array( 'valid' => false, 'error' => 'Server nicht erreichbar: ' . $response->get_error_message() ); } $body = json_decode( wp_remote_retrieve_body( $response ), true ); return is_array( $body ) ? $body : array( 'valid' => false, 'error' => 'Ungültige Server-Antwort.' ); } }