plugin = $plugin; $this->provider_manager = $provider_manager; add_action( 'admin_menu', [ $this, 'register_settings_pages' ] ); add_action( 'admin_init', [ $this, 'register_settings' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_settings_assets' ] ); add_action( 'admin_head', [ $this, 'hide_menu_links' ] ); add_action( 'admin_post_groq_ai_google_oauth_start', [ $this, 'handle_google_oauth_start' ] ); add_action( 'admin_post_groq_ai_google_oauth_callback', [ $this, 'handle_google_oauth_callback' ] ); add_action( 'admin_post_groq_ai_google_oauth_disconnect', [ $this, 'handle_google_oauth_disconnect' ] ); add_action( 'admin_post_groq_ai_save_term_content', [ $this, 'handle_save_term_content' ] ); add_action( 'admin_post_groq_ai_google_test_connection', [ $this, 'handle_google_test_connection' ] ); } public function register_settings_pages() { add_options_page( __( 'Siti AI Productteksten', GROQ_AI_PRODUCT_TEXT_DOMAIN ), __( 'Siti AI', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'manage_options', 'groq-ai-product-text', [ $this, 'render_settings_page' ] ); add_submenu_page( 'options-general.php', __( 'Siti AI Categorie teksten', GROQ_AI_PRODUCT_TEXT_DOMAIN ), __( 'Siti AI Categorieën', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'manage_options', 'groq-ai-product-text-categories', [ $this, 'render_categories_overview_page' ] ); add_submenu_page( 'options-general.php', __( 'Siti AI Merk teksten', GROQ_AI_PRODUCT_TEXT_DOMAIN ), __( 'Siti AI Merken', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'manage_options', 'groq-ai-product-text-brands', [ $this, 'render_brands_overview_page' ] ); add_submenu_page( 'options-general.php', __( 'Siti AI Term tekst', GROQ_AI_PRODUCT_TEXT_DOMAIN ), __( 'Siti AI Term tekst', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'manage_options', 'groq-ai-product-text-term', [ $this, 'render_term_generator_page' ] ); add_submenu_page( 'options-general.php', __( 'Siti AI Modules', GROQ_AI_PRODUCT_TEXT_DOMAIN ), __( 'Siti AI Modules', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'manage_options', 'groq-ai-product-text-modules', [ $this, 'render_modules_page' ] ); add_submenu_page( 'options-general.php', __( 'Siti AI AI-logboek', GROQ_AI_PRODUCT_TEXT_DOMAIN ), __( 'Siti AI AI-logboek', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'manage_options', 'groq-ai-product-text-logs', [ $this, 'render_logs_page' ] ); add_submenu_page( 'options-general.php', __( 'Siti AI Prompt instellingen', GROQ_AI_PRODUCT_TEXT_DOMAIN ), __( 'Siti AI Prompt instellingen', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'manage_options', 'groq-ai-product-text-prompts', [ $this, 'render_prompt_settings_page' ] ); add_submenu_page( 'options-general.php', __( 'Siti AI Log detail', GROQ_AI_PRODUCT_TEXT_DOMAIN ), __( 'Siti AI Log detail', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'manage_options', 'groq-ai-product-text-log', [ $this, 'render_log_detail_page' ] ); } private function get_page_url( $slug = 'groq-ai-product-text', $args = [] ) { $slug = sanitize_key( (string) $slug ); $url = add_query_arg( [ 'page' => $slug, ], admin_url( 'options-general.php' ) ); if ( ! empty( $args ) ) { $url = add_query_arg( $args, $url ); } return $url; } private function get_request_redirect_url( $field, $page_slug = 'groq-ai-product-text' ) { $default = $this->get_page_url( $page_slug ); $value = isset( $_REQUEST[ $field ] ) ? wp_unslash( $_REQUEST[ $field ] ) : ''; if ( '' === $value ) { return $default; } return wp_validate_redirect( $value, $default ); } private function parse_oauth_state( $value ) { $value = (string) $value; if ( '' === $value ) { return []; } $decoded = base64_decode( $value, true ); if ( ! is_string( $decoded ) || '' === $decoded ) { return []; } $data = json_decode( $decoded, true ); return is_array( $data ) ? $data : []; } private function redirect_with_google_notice( $type, $message = '', $redirect = null, $status = 'success' ) { $redirect = $redirect ? $redirect : $this->get_page_url(); $args = [ 'groq_ai_google_notice' => sanitize_key( (string) $type ), 'groq_ai_google_notice_status' => sanitize_key( (string) $status ), ]; if ( '' !== $message ) { $args['groq_ai_google_notice_message'] = rawurlencode( (string) $message ); } wp_safe_redirect( add_query_arg( $args, $redirect ) ); exit; } private function redirect_with_term_notice( $taxonomy, $term_id, $type, $message = '', $status = 'success' ) { $url = ( $taxonomy && $term_id ) ? $this->get_term_page_url( $taxonomy, $term_id ) : $this->get_page_url( 'groq-ai-product-text-categories' ); $args = [ 'groq_ai_term_notice' => sanitize_key( (string) $type ), 'groq_ai_term_status' => sanitize_key( (string) $status ), ]; if ( '' !== $message ) { $args['groq_ai_term_notice_message'] = rawurlencode( (string) $message ); } wp_safe_redirect( add_query_arg( $args, $url ) ); exit; } private function get_google_redirect_uri() { return add_query_arg( 'action', 'groq_ai_google_oauth_callback', admin_url( 'admin-post.php' ) ); } private function update_settings_partial( array $updates ) { $option_key = $this->plugin->get_option_key(); $current = get_option( $option_key, [] ); if ( ! is_array( $current ) ) { $current = []; } foreach ( $updates as $key => $value ) { $current[ $key ] = $value; } update_option( $option_key, $current ); } public function render_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $option_key = $this->plugin->get_option_key(); $settings = $this->plugin->get_settings(); $providers = $this->provider_manager->get_providers(); $current_page = $this->get_page_url(); $prompt_url = $this->get_page_url( 'groq-ai-product-text-prompts' ); $modules_url = $this->get_page_url( 'groq-ai-product-text-modules' ); $logs_url = $this->get_page_url( 'groq-ai-product-text-logs' ); $categories_url = $this->get_page_url( 'groq-ai-product-text-categories' ); $brands_url = $this->get_page_url( 'groq-ai-product-text-brands' ); $prompt_preview = $this->plugin->build_prompt_template_preview( $settings ); $google_notice = isset( $_GET['groq_ai_google_notice'] ) ? sanitize_key( wp_unslash( $_GET['groq_ai_google_notice'] ) ) : ''; $google_status = isset( $_GET['groq_ai_google_notice_status'] ) ? sanitize_key( wp_unslash( $_GET['groq_ai_google_notice_status'] ) ) : ''; $google_message = ''; if ( isset( $_GET['groq_ai_google_notice_message'] ) ) { $google_message = sanitize_text_field( rawurldecode( wp_unslash( $_GET['groq_ai_google_notice_message'] ) ) ); } $google_connected = ! empty( $settings['google_oauth_refresh_token'] ); $google_connected_email = isset( $settings['google_oauth_connected_email'] ) ? (string) $settings['google_oauth_connected_email'] : ''; $google_connected_at = isset( $settings['google_oauth_connected_at'] ) ? absint( $settings['google_oauth_connected_at'] ) : 0; $oauth_redirect = add_query_arg( 'action', 'groq_ai_google_oauth_callback', admin_url( 'admin-post.php' ) ); ?>

get_key(); $option_field = $provider->get_option_key(); $value = isset( $settings[ $option_field ] ) ? (string) $settings[ $option_field ] : ''; ?>

plugin->get_option_key(), $this->plugin->get_option_key(), [ $this->plugin, 'sanitize_settings' ] ); } public function hide_menu_links() { if ( ! current_user_can( 'manage_options' ) ) { return; } ?> brand_taxonomy ) { return $this->brand_taxonomy; } $candidates = [ 'product_brand', 'pwb-brand', 'yith_product_brand', 'berocket_brand', ]; // Attribute-taxonomy fallback (vaak pa_brand). if ( taxonomy_exists( 'pa_brand' ) ) { array_unshift( $candidates, 'pa_brand' ); } $candidates = apply_filters( 'groq_ai_brand_taxonomy_candidates', $candidates ); $found = ''; foreach ( $candidates as $tax ) { $tax = sanitize_key( (string) $tax ); if ( $tax && taxonomy_exists( $tax ) ) { $found = $tax; break; } } $found = apply_filters( 'groq_ai_brand_taxonomy', $found ); $this->brand_taxonomy = sanitize_key( (string) $found ); return $this->brand_taxonomy; } private function get_term_page_url( $taxonomy, $term_id ) { return add_query_arg( [ 'page' => 'groq-ai-product-text-term', 'taxonomy' => sanitize_key( (string) $taxonomy ), 'term_id' => absint( $term_id ), ], admin_url( 'options-general.php' ) ); } private function get_term_overview_data( $taxonomy ) { $taxonomy = sanitize_key( (string) $taxonomy ); if ( isset( $this->term_overview_cache[ $taxonomy ] ) ) { return $this->term_overview_cache[ $taxonomy ]; } $rows = []; $empty_rows = []; if ( '' !== $taxonomy && taxonomy_exists( $taxonomy ) ) { $terms = get_terms( [ 'taxonomy' => $taxonomy, 'hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC', 'number' => 0, ] ); if ( is_wp_error( $terms ) ) { $terms = []; } foreach ( $terms as $term ) { if ( ! $term || ! is_object( $term ) || empty( $term->term_id ) ) { continue; } $words = $this->count_words( isset( $term->description ) ? $term->description : '' ); $has_description = $words > 0; $row = [ 'id' => absint( $term->term_id ), 'name' => (string) $term->name, 'slug' => (string) $term->slug, 'count' => isset( $term->count ) ? absint( $term->count ) : 0, 'words' => $words, 'has_description' => $has_description, 'url' => $this->get_term_page_url( $taxonomy, $term->term_id ), ]; $rows[] = $row; if ( ! $has_description ) { $empty_rows[] = $row; } } } $data = [ 'rows' => $rows, 'empty_rows' => $empty_rows, 'empty_count' => count( $empty_rows ), ]; $this->term_overview_cache[ $taxonomy ] = $data; return $data; } private function render_term_bulk_panel( $label_plural, $empty_count ) { $label_plural = (string) $label_plural; ?>

0 ) { printf( /* translators: 1: amount, 2: label plural (e.g. categorieën) */ esc_html__( 'Er zijn %1$d %2$s zonder omschrijving. Klik op de knop hieronder om automatisch teksten te genereren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), (int) $empty_count, esc_html( $label_plural ) ); } else { printf( esc_html__( 'Alle %s hebben al een omschrijving.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), esc_html( $label_plural ) ); } ?>

    get_term_overview_data( $taxonomy ); $rows = isset( $overview['rows'] ) ? $overview['rows'] : []; $terms = []; foreach ( $rows as $row ) { $terms[] = [ 'id' => isset( $row['id'] ) ? (int) $row['id'] : 0, 'name' => isset( $row['name'] ) ? (string) $row['name'] : '', 'slug' => isset( $row['slug'] ) ? (string) $row['slug'] : '', 'count' => isset( $row['count'] ) ? (int) $row['count'] : 0, 'words' => isset( $row['words'] ) ? (int) $row['words'] : 0, 'hasDescription' => ! empty( $row['has_description'] ), ]; } $defaults = [ 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'groq_ai_bulk_generate_terms' ), 'taxonomy' => $taxonomy, 'terms' => $terms, 'allowRegenerate' => false, 'strings' => [], ]; $config = wp_parse_args( $overrides, $defaults ); wp_localize_script( 'groq-ai-term-bulk', 'GroqAITermBulk', $config ); } public function render_categories_overview_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $taxonomy = 'product_cat'; $overview = $this->get_term_overview_data( $taxonomy ); $rows = isset( $overview['rows'] ) ? $overview['rows'] : []; $empty_count = isset( $overview['empty_count'] ) ? (int) $overview['empty_count'] : 0; ?>

    render_term_bulk_panel( __( 'categorieën', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $empty_count ); ?>
    detect_brand_taxonomy(); if ( '' === $taxonomy ) { ?>

    get_term_overview_data( $taxonomy ); $rows = isset( $overview['rows'] ) ? $overview['rows'] : []; $empty_count = isset( $overview['empty_count'] ) ? (int) $overview['empty_count'] : 0; ?>

    render_term_bulk_panel( __( 'merken', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $empty_count ); ?>

    plugin->get_option_key(); ?>

    plugin ); $logs_table->prepare_items(); ?>

    search_box( __( 'Zoek logboek', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'groq-ai-logs' ); ?> display(); ?>
    get_page_url( 'groq-ai-product-text-logs' ); $log = null; if ( $log_id ) { global $wpdb; $table = $wpdb->prefix . 'groq_ai_generation_logs'; $query = $wpdb->prepare( "SELECT l.*, p.post_title FROM {$table} l LEFT JOIN {$wpdb->posts} p ON p.ID = l.post_id WHERE l.id = %d", $log_id ); $log = $wpdb->get_row( $query, ARRAY_A ); } ?>

    display_name ) : esc_html( (string) $log['user_id'] ); } else { echo '—'; } ?>
    ' . esc_html( $title ) . '' : esc_html( $title ); } else { echo '—'; } ?>

    plugin->get_option_key(); $settings = $this->plugin->get_settings(); $definitions = $this->plugin->get_context_field_definitions(); $context_vals = isset( $settings['context_fields'] ) ? (array) $settings['context_fields'] : $this->plugin->get_default_context_fields(); $image_mode = $this->plugin->get_image_context_mode( $settings ); $image_limit = $this->plugin->get_image_context_limit( $settings ); $preview = $this->plugin->build_prompt_template_preview( $settings ); ?>

    count_words( $term->description ); $meta_prompt = get_term_meta( $term_id, 'groq_ai_term_custom_prompt', true ); $settings = $this->plugin->get_settings(); $bottom_meta_key = $this->resolve_term_bottom_description_meta_key( $term, $settings ); $effective_bottom_meta_key = '' !== $bottom_meta_key ? $bottom_meta_key : 'groq_ai_term_bottom_description'; $bottom_description = (string) get_term_meta( $term_id, $effective_bottom_meta_key, true ); $rankmath_module_enabled = $this->plugin->is_module_enabled( 'rankmath', $settings ); $rankmath_active = $this->plugin->is_rankmath_active(); $rankmath_title = ''; $rankmath_description = ''; $rankmath_focus_keywords = ''; if ( $rankmath_module_enabled ) { $rankmath_keys = $this->resolve_rankmath_term_meta_keys( $term, $settings ); $rankmath_title = (string) get_term_meta( $term_id, $rankmath_keys['title'], true ); $rankmath_description = (string) get_term_meta( $term_id, $rankmath_keys['description'], true ); $rankmath_focus_keywords = (string) get_term_meta( $term_id, $rankmath_keys['focus_keyword'], true ); } $default_prompt = $this->get_term_prompt_text( $term, $meta_prompt ); ?>

    : name ); ?>


     

    
    			
    'rank_math_title', 'description' => 'rank_math_description', 'focus_keyword' => 'rank_math_focus_keyword', ]; $keys = apply_filters( 'groq_ai_rankmath_term_meta_keys', $keys, $term, $settings ); if ( ! is_array( $keys ) ) { $keys = []; } return [ 'title' => isset( $keys['title'] ) ? sanitize_key( (string) $keys['title'] ) : 'rank_math_title', 'description' => isset( $keys['description'] ) ? sanitize_key( (string) $keys['description'] ) : 'rank_math_description', 'focus_keyword' => isset( $keys['focus_keyword'] ) ? sanitize_key( (string) $keys['focus_keyword'] ) : 'rank_math_focus_keyword', ]; } private function get_term_prompt_text( $term, $custom_prompt = null ) { $prompt = ( null !== $custom_prompt ) ? $custom_prompt : ''; if ( null === $custom_prompt && $term && isset( $term->term_id ) ) { $prompt = get_term_meta( $term->term_id, 'groq_ai_term_custom_prompt', true ); } $prompt = trim( (string) $prompt ); if ( '' !== $prompt ) { return $prompt; } $default_prompt = __( 'Schrijf een SEO-vriendelijke categorieomschrijving in het Nederlands. Gebruik duidelijke tussenkoppen en

    -tags. Voeg geen prijsinformatie toe.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); return apply_filters( 'groq_ai_default_term_prompt', $default_prompt, $term ); } public function render_product_attribute_includes_field() { $settings = $this->plugin->get_settings(); $values = isset( $settings['product_attribute_includes'] ) && is_array( $settings['product_attribute_includes'] ) ? $settings['product_attribute_includes'] : []; $values = array_values( array_unique( array_map( 'sanitize_key', $values ) ) ); $options = $this->get_product_attribute_include_options(); ?>

    $label ) : $checked = in_array( $key, $values, true ); ?>
    __( 'Custom attributen (niet-taxonomie)', GROQ_AI_PRODUCT_TEXT_DOMAIN ), ]; if ( function_exists( 'wc_get_attribute_taxonomies' ) ) { $taxonomies = wc_get_attribute_taxonomies(); if ( is_array( $taxonomies ) ) { foreach ( $taxonomies as $attr ) { $name = isset( $attr->attribute_name ) ? sanitize_key( (string) $attr->attribute_name ) : ''; $label = isset( $attr->attribute_label ) ? sanitize_text_field( (string) $attr->attribute_label ) : ''; if ( '' === $name ) { continue; } $taxonomy = 'pa_' . $name; if ( '' === $label ) { $label = function_exists( 'wc_attribute_label' ) ? wc_attribute_label( $taxonomy ) : $taxonomy; } $options[ $taxonomy ] = $label; } } } if ( count( $options ) > 1 ) { $fixed = [ '__custom__' => $options['__custom__'], ]; unset( $options['__custom__'] ); asort( $options, SORT_NATURAL | SORT_FLAG_CASE ); $options = $fixed + $options; } return $options; } public function render_response_format_compat_field() { $settings = $this->plugin->get_settings(); $is_enabled = ! empty( $settings['response_format_compat'] ); ?>

    plugin->get_settings(); $defaults = $this->plugin->get_default_modules_settings(); $modules = isset( $settings['modules'] ) ? $settings['modules'] : $defaults; $config = isset( $modules['rankmath'] ) ? $modules['rankmath'] : ( $defaults['rankmath'] ?? [] ); $rankmath_active = $this->plugin->is_rankmath_active(); $enabled = $rankmath_active && ! empty( $config['enabled'] ); $keyword_limit = isset( $config['focus_keyword_limit'] ) ? absint( $config['focus_keyword_limit'] ) : ( $defaults['rankmath']['focus_keyword_limit'] ?? 3 ); $keyword_limit = $keyword_limit > 0 ? $keyword_limit : 3; $title_pixels = isset( $config['meta_title_pixel_limit'] ) ? absint( $config['meta_title_pixel_limit'] ) : ( $defaults['rankmath']['meta_title_pixel_limit'] ?? 580 ); $title_pixels = $title_pixels > 0 ? $title_pixels : 580; $pixel_limit = isset( $config['meta_description_pixel_limit'] ) ? absint( $config['meta_description_pixel_limit'] ) : ( $defaults['rankmath']['meta_description_pixel_limit'] ?? 920 ); $pixel_limit = $pixel_limit > 0 ? $pixel_limit : 920; $rankmath_active = $this->plugin->is_rankmath_active(); ?>

    />

    />

    />

    admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'groq_ai_generate_term' ), 'taxonomy' => $taxonomy, 'termId' => $term_id, ] ); } $bulk_taxonomy = ''; $bulk_allow_regen = false; $bulk_strings = []; if ( 0 === strpos( (string) $hook, 'settings_page_groq-ai-product-text-categories' ) ) { $bulk_taxonomy = 'product_cat'; $bulk_strings = [ 'statusIdle' => __( 'Bulk gestart. AI werkt de geselecteerde categorieën bij…', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'statusProgress' => __( 'Categorie %1$s van %2$s: %3$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'statusDone' => __( 'Klaar! %d categorieën bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'statusStopped' => __( 'Bulk generatie gestopt. %d categorieën bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'statusEmpty' => __( 'Geen categorieën zonder omschrijving gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'logSuccess' => __( '%1$s gevuld (%2$d woorden).', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'logError' => __( '%1$s mislukt: %2$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'confirmStop' => __( 'Weet je zeker dat je wilt stoppen? De huidige categorie kan onafgemaakt blijven.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), ]; } elseif ( 0 === strpos( (string) $hook, 'settings_page_groq-ai-product-text-brands' ) ) { $detected_taxonomy = $this->detect_brand_taxonomy(); if ( '' !== $detected_taxonomy ) { $bulk_taxonomy = $detected_taxonomy; $bulk_allow_regen = true; $bulk_strings = [ 'statusIdle' => __( 'Bulk gestart. AI werkt de geselecteerde merken bij…', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'statusProgress' => __( 'Merk %1$s van %2$s: %3$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'statusDone' => __( 'Klaar! %d merken bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'statusStopped' => __( 'Bulk generatie gestopt. %d merken bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'statusEmpty' => __( 'Geen merken zonder omschrijving gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'logSuccess' => __( '%1$s gevuld (%2$d woorden).', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'logError' => __( '%1$s mislukt: %2$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'confirmStop' => __( 'Weet je zeker dat je wilt stoppen? Het huidige merk kan onafgemaakt blijven.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'confirmRegenerate' => __( 'Wil je %s opnieuw laten schrijven?', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'regenerateProgress' => __( '%s wordt opnieuw geschreven…', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'regenerateDone' => __( '%s is bijgewerkt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'regenerateError' => __( 'Kon %1$s niet bijwerken: %2$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'regenerateBlocked' => __( 'Wacht tot de bulk generatie klaar is voordat je een merk opnieuw genereert.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), ]; } } if ( '' !== $bulk_taxonomy ) { wp_enqueue_script( 'groq-ai-term-bulk', plugins_url( 'assets/js/term-bulk.js', GROQ_AI_PRODUCT_TEXT_FILE ), [], GROQ_AI_PRODUCT_TEXT_VERSION, true ); $this->localize_term_bulk_script( $bulk_taxonomy, [ 'allowRegenerate' => $bulk_allow_regen, 'strings' => $bulk_strings, ] ); } $current_settings = $this->plugin->get_settings(); $data = [ 'optionKey' => $this->plugin->get_option_key(), 'providers' => [], 'currentProvider' => $current_settings['provider'], 'currentModel' => $current_settings['model'], 'providerRows' => [], 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'refreshNonce' => wp_create_nonce( 'groq_ai_refresh_models' ), 'excludedModels' => Groq_AI_Model_Exclusions::get_all(), 'placeholders' => [ 'selectModel' => __( 'Selecteer een model via "Live modellen ophalen"', GROQ_AI_PRODUCT_TEXT_DOMAIN ), ], ]; foreach ( $this->provider_manager->get_providers() as $provider ) { $provider_key = $provider->get_key(); $cached_models = $this->plugin->get_cached_models_for_provider( $provider_key ); $cached_models = Groq_AI_Model_Exclusions::filter_models( $provider_key, $cached_models ); $data['providers'][ $provider->get_key() ] = [ 'default_label' => sprintf( __( 'Gebruik standaardmodel (%s)', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $provider->get_default_model() ), 'models' => $cached_models, 'supports_live' => $provider->supports_live_models(), ]; $data['providerRows'][ $provider->get_key() ] = 'groq_ai_api_key_' . $provider->get_key(); } wp_localize_script( 'groq-ai-settings', 'GroqAISettingsData', $data ); } public function handle_google_oauth_start() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), '', [ 'response' => 403 ] ); } check_admin_referer( 'groq_ai_google_oauth' ); $redirect = $this->get_request_redirect_url( 'redirect_to' ); $settings = $this->plugin->get_settings(); $client_id = isset( $settings['google_oauth_client_id'] ) ? trim( (string) $settings['google_oauth_client_id'] ) : ''; $client_secret = isset( $settings['google_oauth_client_secret'] ) ? trim( (string) $settings['google_oauth_client_secret'] ) : ''; if ( '' === $client_id || '' === $client_secret ) { $this->redirect_with_google_notice( 'error', __( 'Vul eerst het Google client ID en secret in en sla de instellingen op.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect, 'error' ); } $state_payload = [ 'nonce' => wp_create_nonce( 'groq_ai_google_oauth_state' ), 'redirect' => $redirect, 'timestamp' => time(), ]; $state_json = wp_json_encode( $state_payload ); if ( ! is_string( $state_json ) ) { $state_json = wp_json_encode( (object) [] ); } $state = base64_encode( (string) $state_json ); $scopes = [ 'https://www.googleapis.com/auth/webmasters.readonly', 'https://www.googleapis.com/auth/analytics.readonly', 'https://www.googleapis.com/auth/userinfo.email', ]; $auth_url = add_query_arg( [ 'response_type' => 'code', 'client_id' => $client_id, 'redirect_uri' => $this->get_google_redirect_uri(), 'scope' => implode( ' ', $scopes ), 'access_type' => 'offline', 'prompt' => 'consent', 'include_granted_scopes' => 'true', 'state' => $state, ], 'https://accounts.google.com/o/oauth2/v2/auth' ); wp_safe_redirect( $auth_url ); exit; } public function handle_google_oauth_callback() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), '', [ 'response' => 403 ] ); } $state_value = isset( $_GET['state'] ) ? wp_unslash( $_GET['state'] ) : ''; $state = $this->parse_oauth_state( $state_value ); $redirect = isset( $state['redirect'] ) ? wp_validate_redirect( (string) $state['redirect'], $this->get_page_url() ) : $this->get_page_url(); if ( isset( $_GET['error'] ) ) { $error_message = sanitize_text_field( wp_unslash( $_GET['error'] ) ); if ( isset( $_GET['error_description'] ) ) { $error_message .= ': ' . sanitize_text_field( wp_unslash( $_GET['error_description'] ) ); } $this->redirect_with_google_notice( 'error', $error_message, $redirect, 'error' ); } if ( isset( $state['nonce'] ) && ! wp_verify_nonce( $state['nonce'], 'groq_ai_google_oauth_state' ) ) { $this->redirect_with_google_notice( 'error', __( 'Ongeldige OAuth-sessie. Probeer het opnieuw.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect, 'error' ); } $timestamp = isset( $state['timestamp'] ) ? absint( $state['timestamp'] ) : 0; if ( $timestamp && ( time() - $timestamp ) > HOUR_IN_SECONDS ) { $this->redirect_with_google_notice( 'error', __( 'OAuth-sessie verlopen. Probeer het opnieuw.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect, 'error' ); } $code = isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( $_GET['code'] ) ) : ''; if ( '' === $code ) { $this->redirect_with_google_notice( 'error', __( 'Geen autorisatiecode ontvangen.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect, 'error' ); } $settings = $this->plugin->get_settings(); $client_id = isset( $settings['google_oauth_client_id'] ) ? trim( (string) $settings['google_oauth_client_id'] ) : ''; $client_secret = isset( $settings['google_oauth_client_secret'] ) ? trim( (string) $settings['google_oauth_client_secret'] ) : ''; if ( '' === $client_id || '' === $client_secret ) { $this->redirect_with_google_notice( 'error', __( 'Google client ID en secret ontbreken.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect, 'error' ); } $response = wp_remote_post( 'https://oauth2.googleapis.com/token', [ 'timeout' => 20, 'body' => [ 'code' => $code, 'client_id' => $client_id, 'client_secret' => $client_secret, 'redirect_uri' => $this->get_google_redirect_uri(), 'grant_type' => 'authorization_code', ], ] ); if ( is_wp_error( $response ) ) { $this->redirect_with_google_notice( 'error', $response->get_error_message(), $redirect, 'error' ); } $status_code = wp_remote_retrieve_response_code( $response ); $body = wp_remote_retrieve_body( $response ); $data = json_decode( (string) $body, true ); if ( 200 !== $status_code || ! is_array( $data ) ) { $this->redirect_with_google_notice( 'error', __( 'Kon tokens niet ophalen bij Google.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect, 'error' ); } $access_token = isset( $data['access_token'] ) ? trim( (string) $data['access_token'] ) : ''; $refresh_token = isset( $data['refresh_token'] ) ? trim( (string) $data['refresh_token'] ) : ''; if ( '' === $refresh_token ) { $existing = isset( $settings['google_oauth_refresh_token'] ) ? (string) $settings['google_oauth_refresh_token'] : ''; $refresh_token = $existing; } if ( '' === $refresh_token ) { $this->redirect_with_google_notice( 'error', __( 'Google retourneerde geen refresh token. Forceer toestemming opnieuw en probeer het nogmaals.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect, 'error' ); } if ( '' === $access_token ) { $this->redirect_with_google_notice( 'error', __( 'Google retourneerde geen access token.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect, 'error' ); } $email = ''; $userinfo = wp_remote_get( 'https://openidconnect.googleapis.com/v1/userinfo', [ 'timeout' => 15, 'headers' => [ 'Authorization' => 'Bearer ' . $access_token, ], ] ); if ( ! is_wp_error( $userinfo ) ) { $userinfo_code = wp_remote_retrieve_response_code( $userinfo ); $userinfo_body = json_decode( wp_remote_retrieve_body( $userinfo ), true ); if ( 200 === $userinfo_code && is_array( $userinfo_body ) && isset( $userinfo_body['email'] ) ) { $email = sanitize_email( (string) $userinfo_body['email'] ); } } $this->update_settings_partial( [ 'google_oauth_refresh_token' => sanitize_text_field( $refresh_token ), 'google_oauth_connected_email' => $email, 'google_oauth_connected_at' => current_time( 'timestamp' ), ] ); $this->redirect_with_google_notice( 'connected', __( 'Google OAuth is verbonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect ); } public function handle_google_oauth_disconnect() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), '', [ 'response' => 403 ] ); } check_admin_referer( 'groq_ai_google_disconnect' ); $redirect = $this->get_request_redirect_url( 'redirect_to' ); $this->update_settings_partial( [ 'google_oauth_refresh_token' => '', 'google_oauth_connected_email' => '', 'google_oauth_connected_at' => 0, ] ); $this->redirect_with_google_notice( 'disconnected', __( 'Google OAuth is ontkoppeld.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $redirect ); } public function handle_google_test_connection() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), '', [ 'response' => 403 ] ); } check_admin_referer( 'groq_ai_google_test_connection' ); $redirect = $this->get_request_redirect_url( 'redirect_to' ); $settings = $this->plugin->get_settings(); $oauth_client = new Groq_AI_Google_OAuth_Client(); $token = $oauth_client->get_access_token( $settings ); if ( is_wp_error( $token ) ) { $this->redirect_with_google_notice( 'test', $token->get_error_message(), $redirect, 'error' ); } $messages = [ __( 'OAuth token opgehaald.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ]; $token_info = $oauth_client->get_access_token_info( $token ); if ( ! is_wp_error( $token_info ) && ! empty( $token_info['scope'] ) ) { $messages[] = sprintf( __( 'Scopes: %s', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $token_info['scope'] ); } if ( ! empty( $settings['google_enable_gsc'] ) && ! empty( $settings['google_gsc_site_url'] ) ) { $gsc_client = new Groq_AI_Google_Search_Console_Client( $oauth_client ); $result = $gsc_client->list_sites( $settings ); if ( is_wp_error( $result ) ) { $this->redirect_with_google_notice( 'test', $result->get_error_message(), $redirect, 'error' ); } $messages[] = __( 'Search Console API bereikbaar.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); } if ( ! empty( $settings['google_enable_ga'] ) && ! empty( $settings['google_ga4_property_id'] ) ) { $ga_client = new Groq_AI_Google_Analytics_Data_Client( $oauth_client ); $end_date = gmdate( 'Y-m-d' ); $start_date = gmdate( 'Y-m-d', time() - ( 7 * DAY_IN_SECONDS ) ); $summary = $ga_client->get_property_sessions_summary( $settings, $settings['google_ga4_property_id'], $start_date, $end_date ); if ( is_wp_error( $summary ) ) { $this->redirect_with_google_notice( 'test', $summary->get_error_message(), $redirect, 'error' ); } $sessions = isset( $summary['sessions'] ) ? absint( $summary['sessions'] ) : 0; $messages[] = sprintf( __( 'GA4 API bereikbaar (sessies laatste 7 dagen: %d).', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $sessions ); } $this->redirect_with_google_notice( 'test', implode( ' ', $messages ), $redirect ); } public function handle_save_term_content() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), '', [ 'response' => 403 ] ); } check_admin_referer( 'groq_ai_save_term_content' ); $taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_key( wp_unslash( $_POST['taxonomy'] ) ) : ''; $term_id = isset( $_POST['term_id'] ) ? absint( $_POST['term_id'] ) : 0; if ( '' === $taxonomy || ! taxonomy_exists( $taxonomy ) || ! $term_id ) { $this->redirect_with_term_notice( $taxonomy, $term_id, 'error', __( 'Ongeldige term.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'error' ); } $term = get_term( $term_id, $taxonomy ); if ( ! $term || is_wp_error( $term ) ) { $this->redirect_with_term_notice( $taxonomy, $term_id, 'error', __( 'Term niet gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'error' ); } $description = isset( $_POST['description'] ) ? wp_kses_post( wp_unslash( $_POST['description'] ) ) : ''; $bottom_description = isset( $_POST['groq_ai_term_bottom_description'] ) ? wp_kses_post( wp_unslash( $_POST['groq_ai_term_bottom_description'] ) ) : ''; $custom_prompt = isset( $_POST['groq_ai_term_custom_prompt'] ) ? sanitize_textarea_field( wp_unslash( $_POST['groq_ai_term_custom_prompt'] ) ) : ''; $update = wp_update_term( $term_id, $taxonomy, [ 'description' => $description, ] ); if ( is_wp_error( $update ) ) { $this->redirect_with_term_notice( $taxonomy, $term_id, 'error', $update->get_error_message(), 'error' ); } $settings = $this->plugin->get_settings(); $bottom_meta_key = $this->resolve_term_bottom_description_meta_key( $term, $settings ); $bottom_meta_key = '' !== $bottom_meta_key ? $bottom_meta_key : 'groq_ai_term_bottom_description'; update_term_meta( $term_id, $bottom_meta_key, $bottom_description ); if ( '' === trim( $custom_prompt ) ) { delete_term_meta( $term_id, 'groq_ai_term_custom_prompt' ); } else { update_term_meta( $term_id, 'groq_ai_term_custom_prompt', $custom_prompt ); } if ( isset( $_POST['groq_ai_term_bottom_description'] ) && $bottom_meta_key !== 'groq_ai_term_bottom_description' ) { update_term_meta( $term_id, 'groq_ai_term_bottom_description', $bottom_description ); } if ( $this->plugin->is_module_enabled( 'rankmath', $settings ) ) { $rankmath_keys = $this->resolve_rankmath_term_meta_keys( $term, $settings ); $rankmath_title = isset( $_POST['groq_ai_rankmath_meta_title'] ) ? sanitize_text_field( wp_unslash( $_POST['groq_ai_rankmath_meta_title'] ) ) : ''; $rankmath_description = isset( $_POST['groq_ai_rankmath_meta_description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['groq_ai_rankmath_meta_description'] ) ) : ''; $rankmath_keywords = isset( $_POST['groq_ai_rankmath_focus_keywords'] ) ? sanitize_text_field( wp_unslash( $_POST['groq_ai_rankmath_focus_keywords'] ) ) : ''; update_term_meta( $term_id, $rankmath_keys['title'], $rankmath_title ); update_term_meta( $term_id, $rankmath_keys['description'], $rankmath_description ); update_term_meta( $term_id, $rankmath_keys['focus_keyword'], $rankmath_keywords ); } $this->redirect_with_term_notice( $taxonomy, $term_id, 'saved', __( 'Term opgeslagen.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ); } }