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;
?>
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();
?>
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 );
?>
'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 ) );
}
}