JFIF $ d d 7 * Z qn )ZP 0 !1 A "#a$2Qr D8 a Ri [f \m I ykI w0 c u F c Rı?lO7 к _f˓ [C $殷WF< _W ԣ s Kc ëI zyQy /_LK ℂ ;C",p F A : /] =H ~ , l s / 9 ć: [=/#f ; ) x { ٛE Q )~ = 𘙲r*2~ a _V = ' ku m F D } KYYC )( { * g& f `툪 r y ` = ^ c J. I ]( * `wq 1dđ #̩ ͑ 0 ; H ] u 搂 @:~ ו K L N s h } O I R* 8 :2 ! lD JV o ( 3 = M ( z Ȱ+i *N A r6K n S l ) ! J J Ӂ* % ݉?|D } d5: eP0 R; {$X' x F @ .ÊB {,WJ uQ ɲRI ;9 Q E琯 6 2fT .DU J ;*c P A\IL Nj! J ۱+ O\ ͔ ] ޒ S J ȧ c%A N ol ՎprU L Zԛe r E2= X D Xg VQeӓ k y P7 U* o m Q I s ,K `) 6\G3t?pgjr m ۛجw luGt f h 9uyP 0D;Uڽ"OXl i f $ ) &|ML 0 Z rm1 [ H XP l P R0 'G=i 2N +0 e 2 ]] 9 VT P O 7 h(F * 癈 ' = Q VZD F,d ߬ ~ T X G[ `le 69C R(!S2! P < 0 x < !1AQ "Raq 02Br #S CTb ? Ζ "]mH5WR 7 k.ۛ ! } Q~ + yԏ z |@ T20 S~ K e k *zF f^2X *( @ 8 r ? C I uI|֓>^Ex LgNU Y+{.R Ѫ τV YT D I 6 2 '8Y2 7'\T P.6 d &˦ @V q i |8 -O Ε ] ʔ U= T L 8= ;6c | !qfF3 a ů& ~$ l} 'NW U s$U k^S V : U#6w + +s &r+n ڐ { @ 29gL u " T Ù M=6 (^ "7 r}=6Yݾ lC u hquymp Ǧ G jhs ǜNl ɻ} o 7 #S6 a w 4 ! OS r D 5 7 % | ?x> L | / nD 6?/8 w # [ ) L 7 +6〼T A T g ! %5 Mm Z /c -{1_Je "| ^$ 'O & ޱմ < H vNqϡQ$ < 6 &Nݛ ֨tǻN (9 n ru~'όN sMG& ,uH Kw *Ī 5@ u6 BԒ i YN * f 0 _ GhKW u lT <nj- 0{ M L~ | /a v\ E = ?1 \ɟuq Ђ Y O a K ޡP q $ H8o Q˼EV ~ k 0 h' L Pp M -M" m^ !6l nմ 쮝C U T . ! :vMǨb h ډ Vc r @~p # !1QAa q ? AL0L x LU 4 d ; x " 16ad0 7.\ߋ 0 = P l v/ " c A Ay A|2pO8"d MV #1] = \ " ˡ ] k v I9 d ۓ & q ) ǐq E ꞅ v "P )q X &Q З non]я u ג./ ; gZ$ M QР 2M ]h ni 0 P $ێ 3CɏGjώy : m c # S_ l@ '_ \m|C 2`K _ 1 cIvGc $? PCQ S+D ? K aײ k Fr b 8 ύ NEدg ĝ U 3 6 #Ik+ v { v i ( ވ ? a e) X xy/*@Q Ǽ Ծ ) $ IWO l c G; !i k{ gOŀ L ('x T rb $ w) R $ & N1E td U3Uȉ1p M"N* (DNy d96.(jQ) X 5 c QɎMy W ? Q *!R > 6=7)Xj 5`J ] e 8%t ! + ' ! 1Q 5 !1 AQaq ё #2 "0BRb ? G t^ ## .l lQ T $ v , ,m 㵜5u bV =sY+ @d { N ! dn O < .- B; _w J t 6;Q Jd. Q c%p{ 1 ,sND dF H I0 Гo X ш e黅X ۢ F :)[ F G X ƹ / w _c MeD ,ʡc c . W D tA $ j@ : ) - #u c1< @ۗ9F ) KJ- hpP] _ x [ q B lb p ʖ w q " LFGd ƶ * s +ډ_Zc " ? % t[I P 6 J ]# =ɺ V v v CGs Gh1 > )6 |e y ? L ӣ m,4 G W Ui ` ] uJVo VDG< SB 6ϏQ @ TiU l y O U 0 kfV ~ ~}S Z @ *WU Ui ## ; s /[ = ! 7}" W N ] ' (L ! ~ y5 g9T̅J kbM' +s :S +B) v @ M j e C f j E 0 Y\Qn z G 1 д~ W o{T 9?` Rm yhsy3 !HAD ] m c1 ~ 2 L S u7x T ;j$ `} 4-> L# vzŏ I L S ֭ T { r jGK C ; b pU= - ` Bs K. S F w4 M q] Z dH S 0 ) tLg
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
class BCHAT_Admin {
public function __construct() {
add_action( 'admin_menu', array( $this, 'register_menus' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
add_action( 'admin_head', array( $this, 'hide_internal_submenus_css' ) );
add_action( 'admin_init', array( $this, 'handle_settings_save' ) );
add_filter( 'admin_body_class', array( $this, 'filter_admin_body_class' ) );
}
public function register_menus() {
add_menu_page(
__( 'ON Blog', 'on-blog' ),
__( 'ON Blog', 'on-blog' ),
'edit_posts',
'bchat-dashboard',
array( $this, 'page_dashboard' ),
'dashicons-admin-post',
30
);
add_submenu_page( 'bchat-dashboard', __( 'Blog chat', 'on-blog' ), __( 'Blog chat', 'on-blog' ), 'edit_posts', 'bchat-dashboard', array( $this, 'page_dashboard' ) );
add_submenu_page( 'bchat-dashboard', __( 'Blog optimiseur', 'on-blog' ), __( 'Blog optimiseur', 'on-blog' ), 'edit_posts', 'bchat-optimizer', array( $this, 'page_optimizer' ) );
add_submenu_page( 'bchat-dashboard', __( 'Blog avancé', 'on-blog' ), __( 'Blog avancé', 'on-blog' ), 'edit_posts', 'bchat-advanced', array( $this, 'page_advanced' ) );
// Pages enregistrées avec le parent réel, puis masquées (évite strpos(null) sur PHP 8+)
add_submenu_page( 'bchat-dashboard', __( 'Générer sujet', 'on-blog' ), __( 'Générer sujet', 'on-blog' ), 'edit_posts', 'bchat-topics', array( $this, 'page_topics' ) );
add_submenu_page( 'bchat-dashboard', __( 'Générer article', 'on-blog' ), __( 'Générer article', 'on-blog' ), 'edit_posts', 'bchat-calendar', array( $this, 'page_calendar' ) );
add_submenu_page( 'bchat-dashboard', __( 'Mode rapide', 'on-blog' ), __( 'Mode rapide', 'on-blog' ), 'edit_posts', 'bchat-quick', array( $this, 'page_dashboard' ) );
add_submenu_page( 'bchat-dashboard', __( 'Réglages', 'on-blog' ), __( 'Réglages', 'on-blog' ), 'manage_options','bchat-settings', array( $this, 'page_settings' ) );
add_submenu_page( 'bchat-dashboard', __( 'Aide', 'on-blog' ), __( 'Aide ✦', 'on-blog' ), 'edit_posts', 'bchat-help', array( $this, 'page_help' ) );
add_submenu_page( 'bchat-dashboard', __( 'Pro', 'on-blog' ), __( 'Pro', 'on-blog' ), 'manage_options','bchat-pro', array( $this, 'page_pro' ) );
// Les pages restent enregistrées pour rester accessibles par URL.
}
public function hide_internal_submenus_css() {
if ( ! current_user_can( 'edit_posts' ) ) {
return;
}
$hide = '#toplevel_page_bchat-dashboard .wp-submenu a[href="admin.php?page=bchat-topics"],'
. '#toplevel_page_bchat-dashboard .wp-submenu a[href="admin.php?page=bchat-calendar"],'
. '#toplevel_page_bchat-dashboard .wp-submenu a[href="admin.php?page=bchat-quick"]{display:none !important;}';
if ( get_option( 'bchat_show_advanced_mode', '0' ) === '0' ) {
$hide .= '#toplevel_page_bchat-dashboard .wp-submenu a[href="admin.php?page=bchat-advanced"]{display:none !important;}';
}
if ( get_option( 'bchat_show_optimizer', '0' ) === '0' ) {
$hide .= '#toplevel_page_bchat-dashboard .wp-submenu a[href="admin.php?page=bchat-optimizer"]{display:none !important;}';
}
$hide .= '#toplevel_page_bchat-dashboard .wp-submenu a[href="admin.php?page=bchat-pro"]{color:#c8a96e !important;font-weight:700;}';
$hide .= '#toplevel_page_bchat-dashboard .wp-submenu li.current a[href="admin.php?page=bchat-pro"],#toplevel_page_bchat-dashboard .wp-submenu a[href="admin.php?page=bchat-pro"]:hover{color:#e7c98b !important;}';
$hide .= '#toplevel_page_bchat-dashboard .wp-submenu a[href="admin.php?page=bchat-pro"]::before{content:"★ ";color:#c8a96e;font-weight:700;}';
echo '<style>' . $hide . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS built from internal string literals only, no user input
}
private function is_bchat_admin_page(): bool {
$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
return $page === 'bchat-dashboard' || strpos( $page, 'bchat-' ) === 0;
}
private function get_admin_theme(): string {
$admin_theme = get_option( 'bchat_admin_theme', 'wp' );
$allowed = array( 'wp', 'ivoire' );
if ( ! in_array( $admin_theme, $allowed, true ) ) {
return 'wp';
}
return $admin_theme;
}
public function filter_admin_body_class( $classes ) {
if ( ! $this->is_bchat_admin_page() ) {
return $classes;
}
return trim( $classes . ' bchat-theme-' . sanitize_html_class( $this->get_admin_theme() ) );
}
public function enqueue_assets( $hook ) {
if ( strpos( $hook, 'bchat' ) === false ) return;
$css_ver = (string) @filemtime( BCHAT_PLUGIN_DIR . 'admin/css/admin.css' ) ?: BCHAT_VERSION;
$js_ver = (string) @filemtime( BCHAT_PLUGIN_DIR . 'admin/js/admin.js' ) ?: BCHAT_VERSION;
wp_enqueue_style(
'bchat-admin',
BCHAT_PLUGIN_URL . 'admin/css/admin.css',
array(),
$css_ver
);
wp_enqueue_script(
'bchat-admin',
BCHAT_PLUGIN_URL . 'admin/js/admin.js',
array( 'jquery' ),
$js_ver,
true
);
// Applique le thème sur <html> dès le <head> — évite le flash, même pattern qu'on-galerie.
$admin_theme_val = $this->get_admin_theme();
add_action( 'admin_head', static function() use ( $admin_theme_val ) {
$t = wp_json_encode( $admin_theme_val );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON-encoded trusted internal value
echo '<script>(function(){var t=' . $t . ';'
. 'document.documentElement.setAttribute("data-bchat-theme",t);'
. 'document.addEventListener("DOMContentLoaded",function(){'
. 'document.body.setAttribute("data-bchat-theme",t);'
. 'document.body.classList.add("bchat-theme-"+t);'
. '});})();</script>' . "\n";
} );
wp_localize_script( 'bchat-admin', 'bchat', array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'bchat_nonce' ),
'default_builder' => get_option( 'bchat_default_builder', '' ),
'default_post_status' => get_option( 'bchat_post_status', 'draft' ),
'generation_mode' => get_option( 'bchat_generation_mode', 'assisted' ),
'strings' => array(
'generating' => __( 'Génération en cours…', 'on-blog' ),
'confirm_delete'=> __( 'Supprimer cet élément ?', 'on-blog' ),
'confirm_gen' => __( 'Générer l\'article complet pour ce sujet ?', 'on-blog' ),
'success' => __( 'Succès !', 'on-blog' ),
'error' => __( 'Erreur : ', 'on-blog' ),
),
) );
// Media uploader pour les images placeholder
if ( strpos( $hook, 'bchat-settings' ) !== false ) {
wp_enqueue_media();
}
}
/* ─── Pages ─── */
public function page_dashboard() {
require_once BCHAT_PLUGIN_DIR . 'admin/views/quick.php';
}
public function page_advanced() {
if ( ! BCHAT_Feature_Gate::is_pro() ) {
wp_die( '🔒 ' . esc_html__( 'Blog avancé est réservé au plan Pro.', 'on-blog' ), 'Accès refusé', array( 'response' => 403 ) );
}
require_once BCHAT_PLUGIN_DIR . 'admin/views/dashboard.php';
}
public function page_topics() {
require_once BCHAT_PLUGIN_DIR . 'admin/views/topics.php';
}
public function page_calendar() {
require_once BCHAT_PLUGIN_DIR . 'admin/views/calendar.php';
}
public function page_quick() {
require_once BCHAT_PLUGIN_DIR . 'admin/views/quick.php';
}
public function page_optimizer() {
if ( ! BCHAT_Feature_Gate::is_pro() ) {
wp_die( '🔒 ' . esc_html__( 'Blog optimiseur est réservé au plan Pro.', 'on-blog' ), 'Accès refusé', array( 'response' => 403 ) );
}
require_once BCHAT_PLUGIN_DIR . 'admin/views/optimizer.php';
}
public function page_help() {
require_once BCHAT_PLUGIN_DIR . 'admin/views/help.php';
}
public function page_pro() {
// Render settings directly on Pro tab to avoid late-header redirects.
$_GET['tab'] = 'pro'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- internal admin routing only
require_once BCHAT_PLUGIN_DIR . 'admin/views/settings.php';
}
public function page_diagnostic() {
wp_safe_redirect( admin_url( 'admin.php?page=bchat-settings&tab=diagnostic' ) );
exit;
}
/**
* Traite la sauvegarde des réglages sur admin_init, avant tout output HTML.
* Évite le warning "headers already sent" lors du wp_safe_redirect().
*/
public function handle_settings_save(): void {
if (
! isset( $_POST['bchat_settings_nonce'] ) ||
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['bchat_settings_nonce'] ) ), 'bchat_save_settings' )
) {
return;
}
// Vérifier qu'on est bien sur la page de réglages du plugin.
// Utilise REQUEST pour rester robuste si la query string n'est pas conservée au POST.
$page = isset( $_REQUEST['page'] ) ? sanitize_key( wp_unslash( $_REQUEST['page'] ) ) : '';
if ( $page !== 'bchat-settings' ) {
return;
}
$this->save_settings();
// Recharger le textdomain avec la nouvelle langue immédiatement.
BCHAT_I18n::reload();
$tab = isset( $_REQUEST['tab'] ) ? sanitize_key( wp_unslash( $_REQUEST['tab'] ) ) : 'general';
wp_safe_redirect( admin_url( 'admin.php?page=bchat-settings&tab=' . $tab . '&updated=1' ) );
exit;
}
public function page_settings() {
// La sauvegarde est gérée par handle_settings_save() sur admin_init (avant tout output).
require_once BCHAT_PLUGIN_DIR . 'admin/views/settings.php';
}
private function save_settings() {
// Nonce already verified by caller; re-check here for PHPCS compliance.
if ( ! check_admin_referer( 'bchat_save_settings', 'bchat_settings_nonce' ) ) {
return;
}
$license_action = sanitize_key( wp_unslash( $_POST['bchat_license_action'] ?? '' ) );
if ( $license_action === 'deactivate' ) {
BCHAT_License_Manager::instance()->deactivate();
} elseif ( isset( $_POST['bchat_license_key'] ) ) {
$license_key = sanitize_text_field( wp_unslash( $_POST['bchat_license_key'] ) );
if ( $license_key !== '' ) {
BCHAT_License_Manager::instance()->activate( $license_key );
}
}
$fields = array(
// Clés API
'bchat_anthropic_api_key' => 'text',
'bchat_mistral_api_key' => 'text',
'bchat_openai_api_key' => 'text',
'bchat_google_api_key' => 'text',
'bchat_pexels_api_key' => 'text',
// Modèles
'bchat_anthropic_model' => 'text',
'bchat_mistral_model' => 'text',
'bchat_openai_model' => 'text',
'bchat_google_model' => 'text',
// Modèles image par provider
'bchat_anthropic_image_model' => 'text',
'bchat_mistral_image_model' => 'text',
'bchat_openai_image_model' => 'text',
'bchat_google_image_model' => 'text',
// Endpoints image (providers évolutifs)
'bchat_anthropic_image_endpoint' => 'text',
'bchat_mistral_image_endpoint' => 'text',
// Sélection providers
'bchat_provider_global' => 'text',
'bchat_provider_suggest' => 'text',
'bchat_provider_write' => 'text',
'bchat_provider_image' => 'text',
// Autres
'bchat_article_length' => 'int',
'bchat_default_language' => 'text',
'bchat_plugin_language' => 'text',
'bchat_default_category' => 'int',
'bchat_post_status' => 'text',
'bchat_generation_mode' => 'text',
'bchat_default_builder' => 'text',
'bchat_auto_post_status' => 'text',
'bchat_auto_cadence' => 'text',
'bchat_author_id' => 'int',
'bchat_seo_plugin' => 'text',
);
foreach ( $fields as $key => $type ) {
if ( isset( $_POST[ $key ] ) ) {
$val = $type === 'int' ? intval( wp_unslash( $_POST[ $key ] ) ) : sanitize_text_field( wp_unslash( $_POST[ $key ] ) );
update_option( $key, $val );
}
}
if ( isset( $_POST['bchat_admin_theme'] ) ) {
$admin_theme = sanitize_key( wp_unslash( $_POST['bchat_admin_theme'] ) );
update_option( 'bchat_admin_theme', in_array( $admin_theme, array( 'wp', 'ivoire' ), true ) ? $admin_theme : 'wp' );
}
// Checkboxes (absentes du POST si décochées)
update_option( 'bchat_elementor_include_h1', isset( $_POST['bchat_elementor_include_h1'] ) ? '1' : '0' );
update_option( 'bchat_auto_featured_image', isset( $_POST['bchat_auto_featured_image'] ) ? '1' : '0' );
update_option( 'bchat_show_kpi_grid', isset( $_POST['bchat_show_kpi_grid'] ) ? '1' : '0' );
update_option( 'bchat_show_advanced_mode', isset( $_POST['bchat_show_advanced_mode'] ) ? '1' : '0' );
update_option( 'bchat_show_optimizer', isset( $_POST['bchat_show_optimizer'] ) ? '1' : '0' );
// Mots-clés — SE Ranking
if ( isset( $_POST['bchat_seranking_api_key'] ) ) {
update_option( 'bchat_seranking_api_key', sanitize_text_field( wp_unslash( $_POST['bchat_seranking_api_key'] ) ) );
}
// Mots-clés — Ahrefs
if ( isset( $_POST['bchat_ahrefs_api_key'] ) ) {
update_option( 'bchat_ahrefs_api_key', sanitize_text_field( wp_unslash( $_POST['bchat_ahrefs_api_key'] ) ) );
}
// Mots-clés — DataForSEO
if ( isset( $_POST['bchat_dataforseo_api_key'] ) ) {
update_option( 'bchat_dataforseo_api_key', sanitize_text_field( wp_unslash( $_POST['bchat_dataforseo_api_key'] ) ) );
}
// Images secondaires
if ( isset( $_POST['bchat_inline_images_count'] ) ) {
update_option( 'bchat_inline_images_count', min( 2, max( 0, intval( wp_unslash( $_POST['bchat_inline_images_count'] ) ) ) ) );
}
// Options image IA
if ( isset( $_POST['bchat_auto_image_size'] ) ) {
$allowed_sizes = array( '1024x1024', '1792x1024', '1024x1792' );
$size = sanitize_text_field( wp_unslash( $_POST['bchat_auto_image_size'] ) );
update_option( 'bchat_auto_image_size', in_array( $size, $allowed_sizes, true ) ? $size : '1792x1024' );
}
if ( isset( $_POST['bchat_auto_image_style'] ) ) {
$style = sanitize_text_field( wp_unslash( $_POST['bchat_auto_image_style'] ) );
update_option( 'bchat_auto_image_style', in_array( $style, array( 'natural', 'vivid' ), true ) ? $style : 'natural' );
}
// Images placeholder (JSON array stocké en option)
if ( isset( $_POST['bchat_placeholder_images_json'] ) ) {
$raw = stripslashes( sanitize_textarea_field( wp_unslash( $_POST['bchat_placeholder_images_json'] ) ) );
$decoded = json_decode( $raw, true );
if ( is_array( $decoded ) ) {
update_option( 'bchat_placeholder_images', $decoded );
}
}
if ( ! BCHAT_Feature_Gate::is_pro() ) {
// En Starter, le routage par tache est verrouille sur le moteur global.
update_option( 'bchat_provider_suggest', '' );
update_option( 'bchat_provider_write', '' );
update_option( 'bchat_provider_image', '' );
}
}
}