Dateien nach „bavarian-rank-engine/includes/Admin“ hochladen
This commit is contained in:
parent
52d7c4c39f
commit
24fb4f05ea
3 changed files with 329 additions and 0 deletions
|
|
@ -75,6 +75,15 @@ class AdminMenu {
|
||||||
'bre-robots',
|
'bre-robots',
|
||||||
array( new RobotsPage(), 'render' )
|
array( new RobotsPage(), 'render' )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
'bavarian-rank',
|
||||||
|
__( 'GEO Quick Overview', 'bavarian-rank-engine' ),
|
||||||
|
__( 'GEO Block', 'bavarian-rank-engine' ),
|
||||||
|
'manage_options',
|
||||||
|
'bre-geo',
|
||||||
|
array( new GeoPage(), 'render' )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render_dashboard(): void {
|
public function render_dashboard(): void {
|
||||||
|
|
|
||||||
231
bavarian-rank-engine/includes/Admin/GeoEditorBox.php
Normal file
231
bavarian-rank-engine/includes/Admin/GeoEditorBox.php
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
<?php
|
||||||
|
namespace BavarianRankEngine\Admin;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
use BavarianRankEngine\Features\GeoBlock;
|
||||||
|
|
||||||
|
class GeoEditorBox {
|
||||||
|
public function register(): void {
|
||||||
|
add_action( 'add_meta_boxes', array( $this, 'add_boxes' ) );
|
||||||
|
add_action( 'save_post', array( $this, 'save' ), 10, 2 );
|
||||||
|
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
|
||||||
|
add_action( 'wp_ajax_bre_geo_generate', array( $this, 'ajax_generate' ) );
|
||||||
|
add_action( 'wp_ajax_bre_geo_clear', array( $this, 'ajax_clear' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add_boxes(): void {
|
||||||
|
$settings = GeoBlock::getSettings();
|
||||||
|
foreach ( $settings['post_types'] as $pt ) {
|
||||||
|
add_meta_box(
|
||||||
|
'bre_geo_box',
|
||||||
|
__( 'GEO Quick Overview (BRE)', 'bavarian-rank-engine' ),
|
||||||
|
array( $this, 'render' ),
|
||||||
|
$pt,
|
||||||
|
'normal',
|
||||||
|
'default'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render( \WP_Post $post ): void {
|
||||||
|
$settings = GeoBlock::getSettings();
|
||||||
|
$meta = GeoBlock::getMeta( $post->ID );
|
||||||
|
$enabled = get_post_meta( $post->ID, GeoBlock::META_ENABLED, true );
|
||||||
|
$lock = (bool) get_post_meta( $post->ID, GeoBlock::META_LOCK, true );
|
||||||
|
$generated_at = get_post_meta( $post->ID, GeoBlock::META_GENERATED, true );
|
||||||
|
$prompt_addon = get_post_meta( $post->ID, GeoBlock::META_ADDON, true ) ?: '';
|
||||||
|
$global = SettingsPage::getSettings();
|
||||||
|
$has_api_key = ! empty( $global['api_keys'][ $global['provider'] ] ?? '' );
|
||||||
|
|
||||||
|
wp_nonce_field( 'bre_geo_save_' . $post->ID, 'bre_geo_nonce' );
|
||||||
|
?>
|
||||||
|
<div id="bre-geo-box" data-post-id="<?php echo esc_attr( $post->ID ); ?>"
|
||||||
|
data-nonce="<?php echo esc_attr( wp_create_nonce( 'bre_admin' ) ); ?>">
|
||||||
|
|
||||||
|
<p style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="bre_geo_enabled" value="1"
|
||||||
|
<?php checked( $enabled, '1' ); ?>>
|
||||||
|
<?php esc_html_e( 'Enable GEO block for this post', 'bavarian-rank-engine' ); ?>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="bre_geo_lock" value="1" id="bre-geo-lock"
|
||||||
|
<?php checked( $lock, true ); ?>>
|
||||||
|
<?php esc_html_e( 'Lock auto-regeneration', 'bavarian-rank-engine' ); ?>
|
||||||
|
</label>
|
||||||
|
<?php if ( $generated_at ) : ?>
|
||||||
|
<span style="font-size:11px;color:#666;">
|
||||||
|
<?php
|
||||||
|
// translators: %s = human-readable date
|
||||||
|
printf( esc_html__( 'Generated: %s', 'bavarian-rank-engine' ), esc_html( date_i18n( get_option( 'date_format' ) . ' H:i', (int) $generated_at ) ) );
|
||||||
|
?>
|
||||||
|
</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php if ( $has_api_key ) : ?>
|
||||||
|
<p>
|
||||||
|
<button type="button" class="button" id="bre-geo-generate">
|
||||||
|
<?php
|
||||||
|
empty( $meta['summary'] )
|
||||||
|
? esc_html_e( 'Generate now', 'bavarian-rank-engine' )
|
||||||
|
: esc_html_e( 'Regenerate', 'bavarian-rank-engine' );
|
||||||
|
?>
|
||||||
|
</button>
|
||||||
|
<?php if ( ! empty( $meta['summary'] ) ) : ?>
|
||||||
|
<button type="button" class="button" id="bre-geo-clear" style="margin-left:6px;">
|
||||||
|
<?php esc_html_e( 'Clear', 'bavarian-rank-engine' ); ?>
|
||||||
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
<span id="bre-geo-status" style="margin-left:10px;font-size:12px;"></span>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<p style="margin-bottom:4px;">
|
||||||
|
<label for="bre-geo-summary"><strong><?php esc_html_e( 'Summary', 'bavarian-rank-engine' ); ?></strong></label>
|
||||||
|
</p>
|
||||||
|
<textarea id="bre-geo-summary" name="bre_geo_summary" rows="3"
|
||||||
|
style="width:100%;box-sizing:border-box;"><?php echo esc_textarea( $meta['summary'] ); ?></textarea>
|
||||||
|
|
||||||
|
<p style="margin-bottom:4px;margin-top:10px;">
|
||||||
|
<label for="bre-geo-bullets"><strong><?php esc_html_e( 'Key Points', 'bavarian-rank-engine' ); ?></strong></label>
|
||||||
|
<span style="font-size:11px;color:#666;margin-left:8px;"><?php esc_html_e( '(one per line)', 'bavarian-rank-engine' ); ?></span>
|
||||||
|
</p>
|
||||||
|
<textarea id="bre-geo-bullets" name="bre_geo_bullets" rows="5"
|
||||||
|
style="width:100%;box-sizing:border-box;"><?php echo esc_textarea( implode( "\n", $meta['bullets'] ) ); ?></textarea>
|
||||||
|
|
||||||
|
<p style="margin-bottom:4px;margin-top:10px;">
|
||||||
|
<label for="bre-geo-faq"><strong><?php esc_html_e( 'FAQ', 'bavarian-rank-engine' ); ?></strong></label>
|
||||||
|
<span style="font-size:11px;color:#666;margin-left:8px;"><?php esc_html_e( '(Format: Question? | Answer — one per line)', 'bavarian-rank-engine' ); ?></span>
|
||||||
|
</p>
|
||||||
|
<textarea id="bre-geo-faq" name="bre_geo_faq" rows="4"
|
||||||
|
style="width:100%;box-sizing:border-box;">
|
||||||
|
<?php
|
||||||
|
$faq_lines = array_map(
|
||||||
|
function ( $item ) {
|
||||||
|
return ( $item['q'] ?? '' ) . ' | ' . ( $item['a'] ?? '' );
|
||||||
|
},
|
||||||
|
$meta['faq']
|
||||||
|
);
|
||||||
|
echo esc_textarea( implode( "\n", $faq_lines ) );
|
||||||
|
?>
|
||||||
|
</textarea>
|
||||||
|
|
||||||
|
<?php if ( $settings['allow_prompt_addon'] ) : ?>
|
||||||
|
<p style="margin-bottom:4px;margin-top:10px;">
|
||||||
|
<label for="bre-geo-addon"><strong><?php esc_html_e( 'Prompt add-on (optional)', 'bavarian-rank-engine' ); ?></strong></label>
|
||||||
|
</p>
|
||||||
|
<textarea id="bre-geo-addon" name="bre_geo_prompt_addon" rows="2"
|
||||||
|
style="width:100%;box-sizing:border-box;"><?php echo esc_textarea( $prompt_addon ); ?></textarea>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save( int $post_id, \WP_Post $post ): void {
|
||||||
|
if ( ! isset( $_POST['bre_geo_nonce'] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['bre_geo_nonce'] ) ), 'bre_geo_save_' . $post_id ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-post enabled flag ('' = follow global, '1' = on, '0' = off)
|
||||||
|
$enabled = isset( $_POST['bre_geo_enabled'] ) ? '1' : '0';
|
||||||
|
update_post_meta( $post_id, GeoBlock::META_ENABLED, $enabled );
|
||||||
|
|
||||||
|
$lock = isset( $_POST['bre_geo_lock'] ) ? '1' : '';
|
||||||
|
update_post_meta( $post_id, GeoBlock::META_LOCK, $lock );
|
||||||
|
|
||||||
|
// Manual field edits
|
||||||
|
$summary = sanitize_text_field( wp_unslash( $_POST['bre_geo_summary'] ?? '' ) );
|
||||||
|
update_post_meta( $post_id, GeoBlock::META_SUMMARY, $summary );
|
||||||
|
|
||||||
|
$raw_bullets = sanitize_textarea_field( wp_unslash( $_POST['bre_geo_bullets'] ?? '' ) );
|
||||||
|
$bullets = array_values( array_filter( array_map( 'trim', explode( "\n", $raw_bullets ) ) ) );
|
||||||
|
update_post_meta( $post_id, GeoBlock::META_BULLETS, wp_json_encode( $bullets, JSON_UNESCAPED_UNICODE ) );
|
||||||
|
|
||||||
|
$raw_faq = sanitize_textarea_field( wp_unslash( $_POST['bre_geo_faq'] ?? '' ) );
|
||||||
|
$faq = array();
|
||||||
|
foreach ( array_filter( array_map( 'trim', explode( "\n", $raw_faq ) ) ) as $line ) {
|
||||||
|
$parts = explode( '|', $line, 2 );
|
||||||
|
if ( count( $parts ) === 2 ) {
|
||||||
|
$faq[] = array(
|
||||||
|
'q' => trim( $parts[0] ),
|
||||||
|
'a' => trim( $parts[1] ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_post_meta( $post_id, GeoBlock::META_FAQ, wp_json_encode( $faq, JSON_UNESCAPED_UNICODE ) );
|
||||||
|
|
||||||
|
if ( isset( $_POST['bre_geo_prompt_addon'] ) ) {
|
||||||
|
update_post_meta( $post_id, GeoBlock::META_ADDON, sanitize_textarea_field( wp_unslash( $_POST['bre_geo_prompt_addon'] ) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enqueue( string $hook ): void {
|
||||||
|
if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ), true ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wp_enqueue_script(
|
||||||
|
'bre-geo-editor',
|
||||||
|
BRE_URL . 'assets/geo-editor.js',
|
||||||
|
array( 'jquery' ),
|
||||||
|
BRE_VERSION,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ajax_generate(): void {
|
||||||
|
check_ajax_referer( 'bre_admin', 'nonce' );
|
||||||
|
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||||
|
wp_send_json_error( __( 'Insufficient permissions.', 'bavarian-rank-engine' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$post_id = absint( wp_unslash( $_POST['post_id'] ?? 0 ) );
|
||||||
|
if ( ! $post_id || ! get_post( $post_id ) ) {
|
||||||
|
wp_send_json_error( __( 'Post not found.', 'bavarian-rank-engine' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$geo = new GeoBlock();
|
||||||
|
if ( $geo->generate( $post_id, true ) ) {
|
||||||
|
$meta = GeoBlock::getMeta( $post_id );
|
||||||
|
wp_send_json_success(
|
||||||
|
array(
|
||||||
|
'summary' => $meta['summary'],
|
||||||
|
'bullets' => $meta['bullets'],
|
||||||
|
'faq' => $meta['faq'],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
wp_send_json_error( __( 'Generation failed. Check API key and provider settings.', 'bavarian-rank-engine' ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ajax_clear(): void {
|
||||||
|
check_ajax_referer( 'bre_admin', 'nonce' );
|
||||||
|
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||||
|
wp_send_json_error( __( 'Insufficient permissions.', 'bavarian-rank-engine' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$post_id = absint( wp_unslash( $_POST['post_id'] ?? 0 ) );
|
||||||
|
if ( ! $post_id ) {
|
||||||
|
wp_send_json_error( 'Invalid post ID' );
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_post_meta( $post_id, GeoBlock::META_SUMMARY );
|
||||||
|
delete_post_meta( $post_id, GeoBlock::META_BULLETS );
|
||||||
|
delete_post_meta( $post_id, GeoBlock::META_FAQ );
|
||||||
|
delete_post_meta( $post_id, GeoBlock::META_GENERATED );
|
||||||
|
wp_send_json_success();
|
||||||
|
}
|
||||||
|
}
|
||||||
89
bavarian-rank-engine/includes/Admin/GeoPage.php
Normal file
89
bavarian-rank-engine/includes/Admin/GeoPage.php
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
namespace BavarianRankEngine\Admin;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
use BavarianRankEngine\Features\GeoBlock;
|
||||||
|
|
||||||
|
class GeoPage {
|
||||||
|
public function register(): void {
|
||||||
|
add_action( 'admin_init', array( $this, 'register_settings' ) );
|
||||||
|
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register_settings(): void {
|
||||||
|
register_setting(
|
||||||
|
'bre_geo',
|
||||||
|
GeoBlock::OPTION_KEY,
|
||||||
|
array( 'sanitize_callback' => array( $this, 'sanitize' ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enqueue_assets( string $hook ): void {
|
||||||
|
if ( $hook !== 'bavarian-rank_page_bre-geo' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wp_enqueue_style( 'bre-admin', BRE_URL . 'assets/admin.css', array(), BRE_VERSION );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sanitize( mixed $input ): array {
|
||||||
|
$input = is_array( $input ) ? $input : array();
|
||||||
|
$clean = array();
|
||||||
|
|
||||||
|
$clean['enabled'] = ! empty( $input['enabled'] );
|
||||||
|
$clean['regen_on_update'] = ! empty( $input['regen_on_update'] );
|
||||||
|
$clean['minimal_css'] = ! empty( $input['minimal_css'] );
|
||||||
|
$clean['allow_prompt_addon'] = ! empty( $input['allow_prompt_addon'] );
|
||||||
|
|
||||||
|
$allowed_modes = array( 'auto_on_publish', 'manual_only', 'hybrid' );
|
||||||
|
$clean['mode'] = in_array( $input['mode'] ?? '', $allowed_modes, true )
|
||||||
|
? $input['mode'] : 'auto_on_publish';
|
||||||
|
|
||||||
|
$allowed_positions = array( 'after_first_p', 'top', 'bottom' );
|
||||||
|
$clean['position'] = in_array( $input['position'] ?? '', $allowed_positions, true )
|
||||||
|
? $input['position'] : 'after_first_p';
|
||||||
|
|
||||||
|
$allowed_styles = array( 'details_collapsible', 'open_always', 'store_only_no_frontend' );
|
||||||
|
$clean['output_style'] = in_array( $input['output_style'] ?? '', $allowed_styles, true )
|
||||||
|
? $input['output_style'] : 'details_collapsible';
|
||||||
|
|
||||||
|
$allowed_schemes = array( 'auto', 'light', 'dark' );
|
||||||
|
$clean['color_scheme'] = in_array( $input['color_scheme'] ?? 'auto', $allowed_schemes, true )
|
||||||
|
? $input['color_scheme'] : 'auto';
|
||||||
|
$clean['accent_color'] = sanitize_hex_color( $input['accent_color'] ?? '' ) ?? '';
|
||||||
|
|
||||||
|
$clean['title'] = sanitize_text_field( $input['title'] ?? 'Quick Overview' );
|
||||||
|
$clean['label_summary'] = sanitize_text_field( $input['label_summary'] ?? 'Summary' );
|
||||||
|
$clean['label_bullets'] = sanitize_text_field( $input['label_bullets'] ?? 'Key Points' );
|
||||||
|
$clean['label_faq'] = sanitize_text_field( $input['label_faq'] ?? 'FAQ' );
|
||||||
|
$clean['custom_css'] = sanitize_textarea_field( $input['custom_css'] ?? '' );
|
||||||
|
$clean['prompt_default'] = sanitize_textarea_field(
|
||||||
|
! empty( $input['prompt_default'] ) ? $input['prompt_default'] : GeoBlock::getDefaultPrompt()
|
||||||
|
);
|
||||||
|
$clean['word_threshold'] = max( 50, (int) ( $input['word_threshold'] ?? 350 ) );
|
||||||
|
|
||||||
|
$all_post_types = array_keys( get_post_types( array( 'public' => true ) ) );
|
||||||
|
$clean['post_types'] = array_values(
|
||||||
|
array_intersect(
|
||||||
|
array_map( 'sanitize_key', (array) ( $input['post_types'] ?? array() ) ),
|
||||||
|
$all_post_types
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if ( empty( $clean['post_types'] ) ) {
|
||||||
|
$clean['post_types'] = array( 'post', 'page' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): void {
|
||||||
|
if ( ! current_user_can( 'manage_options' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$settings = GeoBlock::getSettings();
|
||||||
|
$post_types = get_post_types( array( 'public' => true ), 'objects' );
|
||||||
|
include BRE_DIR . 'includes/Admin/views/geo.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue