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' ] ); } /** * Register plugin settings with WordPress. */ public function register_settings() { register_setting( $this->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 ); ?>

    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 ( 'settings_page_groq-ai-product-text-categories' === $hook ) { $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 ( 'settings_page_groq-ai-product-text-brands' === $hook ) { $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 ); } }