141 lines
5.1 KiB
PHP
141 lines
5.1 KiB
PHP
<?php
|
|
namespace BavarianRankEngine\Admin;
|
|
|
|
use BavarianRankEngine\Helpers\KeyVault;
|
|
|
|
class SettingsPage {
|
|
/**
|
|
* Option key for provider + API key data (retains old key name for data continuity).
|
|
*/
|
|
public const OPTION_KEY_PROVIDER = 'bre_settings';
|
|
|
|
/**
|
|
* Option key for meta generator settings.
|
|
*/
|
|
public const OPTION_KEY_META = 'bre_meta_settings';
|
|
|
|
/**
|
|
* Returns merged settings from both option keys with defaults applied.
|
|
* Called by MetaGenerator, SchemaEnhancer, BulkPage, and admin pages.
|
|
*/
|
|
public static function getSettings(): array {
|
|
$defaults = array(
|
|
'provider' => 'openai',
|
|
'api_keys' => array(),
|
|
'models' => array(),
|
|
'meta_auto_enabled' => true,
|
|
'meta_post_types' => array( 'post', 'page' ),
|
|
'token_mode' => 'limit',
|
|
'token_limit' => 1000,
|
|
'prompt' => self::getDefaultPrompt(),
|
|
'schema_enabled' => array(),
|
|
'schema_same_as' => array(),
|
|
'costs' => array(),
|
|
);
|
|
|
|
$saved_provider = get_option( self::OPTION_KEY_PROVIDER, array() );
|
|
$saved_provider = is_array( $saved_provider ) ? $saved_provider : array();
|
|
|
|
$saved_meta = get_option( self::OPTION_KEY_META, array() );
|
|
$saved_meta = is_array( $saved_meta ) ? $saved_meta : array();
|
|
|
|
$settings = array_merge( $defaults, $saved_provider, $saved_meta );
|
|
|
|
foreach ( $settings['api_keys'] as $id => $stored ) {
|
|
$decrypted = KeyVault::decrypt( $stored );
|
|
// Fallback: if decrypt returns empty, the stored value is a legacy plain-text key
|
|
$settings['api_keys'][ $id ] = $decrypted !== '' ? $decrypted : $stored;
|
|
}
|
|
|
|
return $settings;
|
|
}
|
|
|
|
public static function getDefaultPrompt(): string {
|
|
return 'Schreibe eine SEO-optimierte Meta-Beschreibung für den folgenden Artikel.' . "\n"
|
|
. 'Die Beschreibung soll für menschliche Leser verständlich und hilfreich sein,' . "\n"
|
|
. 'den Inhalt treffend zusammenfassen und zwischen 150 und 160 Zeichen lang sein.' . "\n"
|
|
. 'Schreibe die Meta-Beschreibung auf {language}.' . "\n"
|
|
. 'Antworte ausschließlich mit der Meta-Beschreibung, ohne Erklärung.' . "\n\n"
|
|
. 'Titel: {title}' . "\n"
|
|
. 'Inhalt: {content}';
|
|
}
|
|
|
|
/**
|
|
* Kept for backwards compatibility — used by BulkPage and tests.
|
|
* Validates and sanitises a combined settings array (provider + meta fields).
|
|
*/
|
|
public function sanitize_settings( mixed $input ): array {
|
|
$input = is_array( $input ) ? $input : array();
|
|
$raw_existing = get_option( self::OPTION_KEY_PROVIDER, array() );
|
|
$existing = is_array( $raw_existing ) ? $raw_existing : array();
|
|
$clean = array();
|
|
|
|
$clean['provider'] = sanitize_key( $input['provider'] ?? 'openai' );
|
|
$clean['meta_auto_enabled'] = ! empty( $input['meta_auto_enabled'] );
|
|
$clean['token_mode'] = in_array( $input['token_mode'] ?? '', array( 'limit', 'full' ), true )
|
|
? $input['token_mode'] : 'limit';
|
|
$clean['token_limit'] = max( 100, (int) ( $input['token_limit'] ?? 1000 ) );
|
|
$clean['prompt'] = sanitize_textarea_field( $input['prompt'] ?? self::getDefaultPrompt() );
|
|
|
|
$clean['api_keys'] = array();
|
|
foreach ( ( $input['api_keys'] ?? array() ) as $provider_id => $raw ) {
|
|
$provider_id = sanitize_key( $provider_id );
|
|
$raw = sanitize_text_field( $raw );
|
|
if ( $raw !== '' ) {
|
|
$clean['api_keys'][ $provider_id ] = KeyVault::encrypt( $raw );
|
|
} elseif ( isset( $existing['api_keys'][ $provider_id ] ) ) {
|
|
$clean['api_keys'][ $provider_id ] = $existing['api_keys'][ $provider_id ];
|
|
}
|
|
}
|
|
|
|
$clean['models'] = array();
|
|
foreach ( ( $input['models'] ?? array() ) as $provider_id => $model ) {
|
|
$clean['models'][ sanitize_key( $provider_id ) ] = sanitize_text_field( $model );
|
|
}
|
|
|
|
$clean['costs'] = array();
|
|
foreach ( ( $input['costs'] ?? array() ) as $provider_id => $models ) {
|
|
$provider_id = sanitize_key( $provider_id );
|
|
foreach ( (array) $models as $model_id => $prices ) {
|
|
$model_id = sanitize_text_field( $model_id );
|
|
$clean['costs'][ $provider_id ][ $model_id ] = array(
|
|
'input' => max( 0.0, (float) ( $prices['input'] ?? 0 ) ),
|
|
'output' => max( 0.0, (float) ( $prices['output'] ?? 0 ) ),
|
|
);
|
|
}
|
|
}
|
|
|
|
$all_post_types = array_keys( get_post_types( array( 'public' => true ) ) );
|
|
$clean['meta_post_types'] = array_values(
|
|
array_intersect(
|
|
array_map( 'sanitize_key', (array) ( $input['meta_post_types'] ?? array() ) ),
|
|
$all_post_types
|
|
)
|
|
);
|
|
|
|
$schema_types = array( 'organization', 'author', 'speakable', 'article_about', 'breadcrumb', 'ai_meta_tags' );
|
|
$clean['schema_enabled'] = array_values(
|
|
array_intersect(
|
|
array_map( 'sanitize_key', (array) ( $input['schema_enabled'] ?? array() ) ),
|
|
$schema_types
|
|
)
|
|
);
|
|
|
|
$org_raw = $input['schema_same_as']['organization'] ?? '';
|
|
if ( is_array( $org_raw ) ) {
|
|
$org_raw = implode( "\n", $org_raw );
|
|
}
|
|
$clean['schema_same_as'] = array(
|
|
'organization' => array_values(
|
|
array_filter(
|
|
array_map(
|
|
'esc_url_raw',
|
|
array_map( 'trim', explode( "\n", $org_raw ) )
|
|
)
|
|
)
|
|
),
|
|
);
|
|
|
|
return $clean;
|
|
}
|
|
}
|