release: v1.3.0
This commit is contained in:
parent
5139e5ad29
commit
a6043fe28a
7 changed files with 91 additions and 29 deletions
|
|
@ -304,6 +304,7 @@ define( 'BREZNGEO_OPENAI_KEY', 'sk-...' );
|
|||
define( 'BREZNGEO_ANTHROPIC_KEY', 'sk-ant-...' );
|
||||
define( 'BREZNGEO_GEMINI_KEY', 'AI...' );
|
||||
define( 'BREZNGEO_GROK_KEY', 'xai-...' );
|
||||
define( 'BREZNGEO_OPENROUTER_KEY', 'sk-or-...' );
|
||||
```
|
||||
|
||||
### CSRF-Schutz und Capability Checks
|
||||
|
|
|
|||
|
|
@ -413,6 +413,7 @@ define( 'BREZNGEO_OPENAI_KEY', 'sk-...' );
|
|||
define( 'BREZNGEO_ANTHROPIC_KEY', 'sk-ant-...' );
|
||||
define( 'BREZNGEO_GEMINI_KEY', 'AI...' );
|
||||
define( 'BREZNGEO_GROK_KEY', 'xai-...' );
|
||||
define( 'BREZNGEO_OPENROUTER_KEY', 'sk-or-...' );
|
||||
```
|
||||
|
||||
In the admin UI, keys are always displayed masked: `••••••Ab3c9` (only the last 5 characters visible).
|
||||
|
|
|
|||
|
|
@ -74,6 +74,13 @@ class ProviderPage {
|
|||
$clean['api_keys'][ $provider_id ] = $existing['api_keys'][ $provider_id ];
|
||||
}
|
||||
}
|
||||
// Preserve DB-stored keys for providers whose UI field was disabled
|
||||
// (wp-config.php constant override) and therefore never submitted.
|
||||
foreach ( ( $existing['api_keys'] ?? array() ) as $provider_id => $stored ) {
|
||||
if ( ! isset( $clean['api_keys'][ $provider_id ] ) ) {
|
||||
$clean['api_keys'][ $provider_id ] = $stored;
|
||||
}
|
||||
}
|
||||
|
||||
$clean['models'] = array();
|
||||
foreach ( ( $input['models'] ?? array() ) as $provider_id => $model ) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ class SettingsPage {
|
|||
*/
|
||||
public const OPTION_KEY_SCHEMA = 'brezngeo_schema_settings';
|
||||
|
||||
/**
|
||||
* Provider IDs that support a wp-config.php constant override.
|
||||
* A defined constant wins over the DB value and locks the UI field.
|
||||
*/
|
||||
private const CONSTANT_KEY_PROVIDERS = array( 'openai', 'anthropic', 'gemini', 'grok', 'openrouter' );
|
||||
|
||||
/**
|
||||
* Returns merged settings from both option keys with defaults applied.
|
||||
* Called by MetaGenerator, SchemaEnhancer, BulkPage, and admin pages.
|
||||
|
|
@ -62,9 +68,37 @@ class SettingsPage {
|
|||
$settings['api_keys'][ $id ] = $decrypted !== '' ? $decrypted : $stored;
|
||||
}
|
||||
|
||||
// wp-config.php constants override the DB value and lock the admin field.
|
||||
$settings['api_keys_locked'] = array();
|
||||
foreach ( self::CONSTANT_KEY_PROVIDERS as $provider_id ) {
|
||||
$constant = self::constantNameForProvider( $provider_id );
|
||||
if ( $constant !== '' && defined( $constant ) ) {
|
||||
$settings['api_keys'][ $provider_id ] = (string) constant( $constant );
|
||||
$settings['api_keys_locked'][ $provider_id ] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wp-config.php constant name for a given provider ID,
|
||||
* or empty string if the provider has no constant override.
|
||||
*
|
||||
* Must be pure — no WP hooks, no option reads. Called once per entry
|
||||
* in CONSTANT_KEY_PROVIDERS during getSettings().
|
||||
*
|
||||
* The website (de/index.html, howto.html) already promises the names
|
||||
* BREZNGEO_OPENAI_KEY, BREZNGEO_ANTHROPIC_KEY, BREZNGEO_GEMINI_KEY,
|
||||
* BREZNGEO_GROK_KEY and BREZNGEO_OPENROUTER_KEY — those are the contract.
|
||||
*/
|
||||
private static function constantNameForProvider( string $provider_id ): string {
|
||||
if ( ! in_array( $provider_id, self::CONSTANT_KEY_PROVIDERS, true ) ) {
|
||||
return '';
|
||||
}
|
||||
return 'BREZNGEO_' . strtoupper( $provider_id ) . '_KEY';
|
||||
}
|
||||
|
||||
public static function getDefaultPrompt(): string {
|
||||
$locale = get_locale();
|
||||
$is_german = str_starts_with( $locale, 'de_' );
|
||||
|
|
|
|||
|
|
@ -9,31 +9,31 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
exit;
|
||||
}
|
||||
|
||||
$or_models = $provider->getModels();
|
||||
$or_saved_model = $settings['models']['openrouter'] ?? '';
|
||||
$or_is_custom = $or_saved_model !== '' && ! array_key_exists( $or_saved_model, $or_models );
|
||||
$or_cached_pricing = get_transient( \BreznGEO\Providers\OpenRouterProvider::MODELS_CACHE );
|
||||
$or_cache_is_array = is_array( $or_cached_pricing );
|
||||
$or_selected_pricing = ( $or_cache_is_array && isset( $or_cached_pricing[ $or_saved_model ] ) )
|
||||
? $or_cached_pricing[ $or_saved_model ]
|
||||
$brezngeo_or_models = $provider->getModels();
|
||||
$brezngeo_or_saved_model = $settings['models']['openrouter'] ?? '';
|
||||
$brezngeo_or_is_custom = $brezngeo_or_saved_model !== '' && ! array_key_exists( $brezngeo_or_saved_model, $brezngeo_or_models );
|
||||
$brezngeo_or_cached_pricing = get_transient( \BreznGEO\Providers\OpenRouterProvider::MODELS_CACHE );
|
||||
$brezngeo_or_cache_is_array = is_array( $brezngeo_or_cached_pricing );
|
||||
$brezngeo_or_selected_pricing = ( $brezngeo_or_cache_is_array && isset( $brezngeo_or_cached_pricing[ $brezngeo_or_saved_model ] ) )
|
||||
? $brezngeo_or_cached_pricing[ $brezngeo_or_saved_model ]
|
||||
: null;
|
||||
?>
|
||||
<br><br>
|
||||
<label><?php esc_html_e( 'Model:', 'brezngeo' ); ?></label>
|
||||
<select name="brezngeo_settings[models][openrouter]" class="brezngeo-openrouter-model-select" id="brezngeo-openrouter-model">
|
||||
<?php if ( empty( $or_models ) ) : ?>
|
||||
<?php if ( empty( $brezngeo_or_models ) ) : ?>
|
||||
<option value=""><?php esc_html_e( 'No models loaded yet — click "Load models"', 'brezngeo' ); ?></option>
|
||||
<?php else : ?>
|
||||
<?php foreach ( $or_models as $or_mid => $or_label ) : ?>
|
||||
<option value="<?php echo esc_attr( $or_mid ); ?>"
|
||||
<?php selected( $or_saved_model, $or_mid ); ?>
|
||||
data-input="<?php echo esc_attr( isset( $or_cached_pricing[ $or_mid ]['input_cost'] ) ? $or_cached_pricing[ $or_mid ]['input_cost'] : '' ); ?>"
|
||||
data-output="<?php echo esc_attr( isset( $or_cached_pricing[ $or_mid ]['output_cost'] ) ? $or_cached_pricing[ $or_mid ]['output_cost'] : '' ); ?>">
|
||||
<?php echo esc_html( $or_label ); ?>
|
||||
<?php foreach ( $brezngeo_or_models as $brezngeo_or_mid => $brezngeo_or_label ) : ?>
|
||||
<option value="<?php echo esc_attr( $brezngeo_or_mid ); ?>"
|
||||
<?php selected( $brezngeo_or_saved_model, $brezngeo_or_mid ); ?>
|
||||
data-input="<?php echo esc_attr( isset( $brezngeo_or_cached_pricing[ $brezngeo_or_mid ]['input_cost'] ) ? $brezngeo_or_cached_pricing[ $brezngeo_or_mid ]['input_cost'] : '' ); ?>"
|
||||
data-output="<?php echo esc_attr( isset( $brezngeo_or_cached_pricing[ $brezngeo_or_mid ]['output_cost'] ) ? $brezngeo_or_cached_pricing[ $brezngeo_or_mid ]['output_cost'] : '' ); ?>">
|
||||
<?php echo esc_html( $brezngeo_or_label ); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
<option value="__custom__" <?php selected( $or_is_custom ); ?>>
|
||||
<option value="__custom__" <?php selected( $brezngeo_or_is_custom ); ?>>
|
||||
<?php esc_html_e( 'Custom model ID…', 'brezngeo' ); ?>
|
||||
</option>
|
||||
</select>
|
||||
|
|
@ -42,14 +42,14 @@ $or_selected_pricing = ( $or_cache_is_array && isset( $or_cached_pricing[ $or_sa
|
|||
</button>
|
||||
<span class="brezngeo-openrouter-load-status" aria-live="polite"></span>
|
||||
|
||||
<div class="brezngeo-openrouter-custom-wrap" style="<?php echo $or_is_custom ? '' : 'display:none;'; ?>margin-top:10px;">
|
||||
<div class="brezngeo-openrouter-custom-wrap" style="<?php echo $brezngeo_or_is_custom ? '' : 'display:none;'; ?>margin-top:10px;">
|
||||
<label for="brezngeo-openrouter-custom">
|
||||
<?php esc_html_e( 'Custom model ID:', 'brezngeo' ); ?>
|
||||
</label>
|
||||
<input type="text"
|
||||
id="brezngeo-openrouter-custom"
|
||||
name="brezngeo_settings[openrouter_custom_model]"
|
||||
value="<?php echo esc_attr( $or_is_custom ? $or_saved_model : '' ); ?>"
|
||||
value="<?php echo esc_attr( $brezngeo_or_is_custom ? $brezngeo_or_saved_model : '' ); ?>"
|
||||
placeholder="<?php esc_attr_e( 'e.g. anthropic/claude-opus-4.7', 'brezngeo' ); ?>"
|
||||
class="regular-text">
|
||||
<p class="description">
|
||||
|
|
@ -69,11 +69,11 @@ $or_selected_pricing = ( $or_cache_is_array && isset( $or_cached_pricing[ $or_sa
|
|||
|
||||
<p style="margin-top:12px;"><strong><?php esc_html_e( 'Pricing (automatically from OpenRouter, per 1M tokens):', 'brezngeo' ); ?></strong></p>
|
||||
<div class="brezngeo-openrouter-pricing-display" id="brezngeo-openrouter-pricing" style="font-size:12px;color:#555;">
|
||||
<?php if ( $or_selected_pricing ) : ?>
|
||||
Input $<span class="or-price-input"><?php echo esc_html( number_format( (float) $or_selected_pricing['input_cost'], 4 ) ); ?></span>
|
||||
/ 1M · Output $<span class="or-price-output"><?php echo esc_html( number_format( (float) $or_selected_pricing['output_cost'], 4 ) ); ?></span>
|
||||
<?php if ( $brezngeo_or_selected_pricing ) : ?>
|
||||
Input $<span class="or-price-input"><?php echo esc_html( number_format( (float) $brezngeo_or_selected_pricing['input_cost'], 4 ) ); ?></span>
|
||||
/ 1M · Output $<span class="or-price-output"><?php echo esc_html( number_format( (float) $brezngeo_or_selected_pricing['output_cost'], 4 ) ); ?></span>
|
||||
/ 1M
|
||||
<?php elseif ( $or_is_custom ) : ?>
|
||||
<?php elseif ( $brezngeo_or_is_custom ) : ?>
|
||||
<em><?php esc_html_e( 'Pricing unknown for custom models — will be populated after you click "Load models".', 'brezngeo' ); ?></em>
|
||||
<?php else : ?>
|
||||
<em><?php esc_html_e( 'Click "Load models" to fetch pricing from OpenRouter.', 'brezngeo' ); ?></em>
|
||||
|
|
|
|||
|
|
@ -36,9 +36,26 @@
|
|||
</td>
|
||||
</tr>
|
||||
<?php foreach ( $providers as $id => $provider ) : // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound ?>
|
||||
<?php $locked = ! empty( $settings['api_keys_locked'][ $id ] ); ?>
|
||||
<tr class="brezngeo-provider-row" data-provider="<?php echo esc_attr( $id ); ?>">
|
||||
<th scope="row"><?php echo esc_html( $provider->getName() ); ?> <?php esc_html_e( 'API Key', 'brezngeo' ); ?></th>
|
||||
<td>
|
||||
<?php if ( $locked ) : ?>
|
||||
<span class="brezngeo-key-saved">
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %s: wp-config.php constant name */
|
||||
esc_html__( 'Loaded from wp-config.php: %s', 'brezngeo' ),
|
||||
'<code>BREZNGEO_' . esc_html( strtoupper( $id ) ) . '_KEY</code>'
|
||||
);
|
||||
?>
|
||||
</span><br>
|
||||
<input type="password" value="" placeholder="—" class="regular-text" disabled>
|
||||
<button type="button" class="button brezngeo-test-btn" data-provider="<?php echo esc_attr( $id ); ?>">
|
||||
<?php esc_html_e( 'Test connection', 'brezngeo' ); ?>
|
||||
</button>
|
||||
<span class="brezngeo-test-result" id="test-result-<?php echo esc_attr( $id ); ?>"></span>
|
||||
<?php else : ?>
|
||||
<?php if ( ! empty( $masked_keys[ $id ] ) ) : ?>
|
||||
<span class="brezngeo-key-saved">
|
||||
<?php esc_html_e( 'Saved:', 'brezngeo' ); ?> <code><?php echo esc_html( $masked_keys[ $id ] ); ?></code>
|
||||
|
|
@ -54,6 +71,7 @@
|
|||
<?php esc_html_e( 'Test connection', 'brezngeo' ); ?>
|
||||
</button>
|
||||
<span class="brezngeo-test-result" id="test-result-<?php echo esc_attr( $id ); ?>"></span>
|
||||
<?php endif; ?>
|
||||
<?php if ( $id === 'openrouter' ) : ?>
|
||||
<?php include BREZNGEO_DIR . 'includes/Admin/views/partials/openrouter-model-field.php'; ?>
|
||||
<?php else : ?>
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@ No data is transmitted during normal page loads or to visitors.
|
|||
* New: OpenRouter as a fifth AI provider — access to 600+ models (Claude, GPT, Gemini, Llama, Mistral, DeepSeek, and more) through a single API key.
|
||||
* New: On-demand model loader fetches OpenRouter's curated Marketing/SEO model list with live per-model pricing.
|
||||
* New: Custom model ID field lets you route to any OpenRouter model (e.g. anthropic/claude-opus-4.7) without waiting for a plugin update.
|
||||
* New: OpenRouter joins the wp-config.php key-mapping (BREZNGEO_OPENROUTER_KEY) — define any provider key as a constant to keep it out of the database entirely; the admin field becomes read-only.
|
||||
|
||||
= 1.2.2 =
|
||||
* i18n: Added explicit load_plugin_textdomain() call for reliable translation loading on ClassicPress and other WordPress derivatives.
|
||||
|
|
|
|||
Loading…
Reference in a new issue