Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -136,28 +136,15 @@ class Groq_AI_Logs_Table extends WP_List_Table {
|
||||
|
||||
protected function column_created_at( $item ) {
|
||||
$date = esc_html( mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $item['created_at'] ) );
|
||||
$usage = $this->get_usage_meta( $item );
|
||||
$payload = [
|
||||
'created_at' => $item['created_at'],
|
||||
'user' => $this->column_default( $item, 'user_id' ),
|
||||
'post_title' => $item['post_title'],
|
||||
'provider' => $item['provider'],
|
||||
'model' => $item['model'],
|
||||
'status' => $item['status'],
|
||||
'tokens_prompt' => isset( $item['tokens_prompt'] ) ? (int) $item['tokens_prompt'] : null,
|
||||
'tokens_completion' => isset( $item['tokens_completion'] ) ? (int) $item['tokens_completion'] : null,
|
||||
'tokens_total' => isset( $item['tokens_total'] ) ? (int) $item['tokens_total'] : null,
|
||||
'prompt' => $item['prompt'],
|
||||
'response' => $item['response'],
|
||||
'error_message' => $item['error_message'],
|
||||
'image_context' => isset( $usage['image_context'] ) ? $usage['image_context'] : null,
|
||||
];
|
||||
$encoded = esc_attr( wp_json_encode( $payload ) );
|
||||
return sprintf(
|
||||
'<a href="#" class="groq-ai-log-row" data-groq-log="%s">%s</a>',
|
||||
$encoded,
|
||||
$date
|
||||
$url = add_query_arg(
|
||||
[
|
||||
'page' => 'groq-ai-product-text-log',
|
||||
'log_id' => isset( $item['id'] ) ? (int) $item['id'] : 0,
|
||||
],
|
||||
admin_url( 'options-general.php' )
|
||||
);
|
||||
|
||||
return sprintf( '<a href="%s">%s</a>', esc_url( $url ), $date );
|
||||
}
|
||||
|
||||
private function get_usage_meta( $item ) {
|
||||
|
||||
@@ -84,6 +84,311 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
[ $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;
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e( 'Siti AI instellingen', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Kies je AI-aanbieder, beheer API-sleutels en koppel optioneel Google Search Console/Analytics voor extra context.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?>
|
||||
</p>
|
||||
<p style="margin:16px 0; display:flex; flex-wrap:wrap; gap:8px;">
|
||||
<a class="button" href="<?php echo esc_url( $prompt_url ); ?>"><?php esc_html_e( 'Prompt instellingen', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></a>
|
||||
<a class="button" href="<?php echo esc_url( $modules_url ); ?>"><?php esc_html_e( 'Modules', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></a>
|
||||
<a class="button" href="<?php echo esc_url( $logs_url ); ?>"><?php esc_html_e( 'AI-logboek', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></a>
|
||||
<a class="button" href="<?php echo esc_url( $categories_url ); ?>"><?php esc_html_e( 'Categorie teksten', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></a>
|
||||
<a class="button" href="<?php echo esc_url( $brands_url ); ?>"><?php esc_html_e( 'Merk teksten', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></a>
|
||||
</p>
|
||||
<?php settings_errors( $option_key ); ?>
|
||||
<?php if ( $google_notice ) :
|
||||
$class = ( 'error' === $google_status ) ? 'notice-error' : 'notice-success';
|
||||
$google_message = '' !== $google_message ? $google_message : ( 'connected' === $google_notice ? __( 'Google OAuth is verbonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) : ( 'disconnected' === $google_notice ? __( 'Google OAuth is ontkoppeld.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) : __( 'Google test afgerond.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ) );
|
||||
?>
|
||||
<div class="notice <?php echo esc_attr( $class ); ?>"><p><?php echo esc_html( $google_message ); ?></p></div>
|
||||
<?php endif; ?>
|
||||
<div style="margin:16px 0; padding:16px; background:#fff; border:1px solid #dcdcde;">
|
||||
<strong><?php esc_html_e( 'Huidige promptcontext', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></strong>
|
||||
<pre style="background:#f6f7f7; padding:12px; overflow:auto; margin-top:8px; white-space:pre-wrap;"><?php echo esc_html( $prompt_preview ); ?></pre>
|
||||
</div>
|
||||
<form method="post" action="options.php">
|
||||
<?php settings_fields( $option_key ); ?>
|
||||
<h2><?php esc_html_e( 'AI-aanbieder', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h2>
|
||||
<table class="form-table" role="presentation">
|
||||
<tr>
|
||||
<th scope="row"><label for="groq-ai-provider"><?php esc_html_e( 'Aanbieder', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<select id="groq-ai-provider" name="<?php echo esc_attr( $option_key ); ?>[provider]">
|
||||
<?php foreach ( $providers as $provider ) :
|
||||
$provider_key = $provider->get_key();
|
||||
?>
|
||||
<option value="<?php echo esc_attr( $provider_key ); ?>" <?php selected( $settings['provider'], $provider_key ); ?>><?php echo esc_html( $provider->get_label() ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<p class="description"><?php esc_html_e( 'Selecteer welke aanbieder de product- en termteksten schrijft.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="groq-ai-model-select"><?php esc_html_e( 'Model', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<div class="groq-ai-model-field">
|
||||
<select id="groq-ai-model-select" name="<?php echo esc_attr( $option_key ); ?>[model]" data-current-model="<?php echo esc_attr( isset( $settings['model'] ) ? $settings['model'] : '' ); ?>">
|
||||
<option value="" selected="selected"><?php esc_html_e( 'Selecteer eerst een aanbieder', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="button" class="button" id="groq-ai-refresh-models"><?php esc_html_e( 'Live modellen ophalen', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></button>
|
||||
<p id="groq-ai-refresh-models-status" class="description"></p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php foreach ( $providers as $provider ) :
|
||||
$provider_key = $provider->get_key();
|
||||
$option_field = $provider->get_option_key();
|
||||
$value = isset( $settings[ $option_field ] ) ? (string) $settings[ $option_field ] : '';
|
||||
?>
|
||||
<tr id="groq_ai_api_key_<?php echo esc_attr( $provider_key ); ?>" data-provider-row="<?php echo esc_attr( $provider_key ); ?>">
|
||||
<th scope="row"><label for="groq-ai-api-<?php echo esc_attr( $provider_key ); ?>"><?php esc_html_e( 'API-sleutel', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<input type="password" id="groq-ai-api-<?php echo esc_attr( $provider_key ); ?>" class="regular-text" name="<?php echo esc_attr( $option_key ); ?>[<?php echo esc_attr( $option_field ); ?>]" value="<?php echo esc_attr( $value ); ?>" autocomplete="off" />
|
||||
<p class="description"><?php printf( esc_html__( 'Voer de API-sleutel in voor %s.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), esc_html( $provider->get_label() ) ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
<h2><?php esc_html_e( 'Algemene instellingen', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h2>
|
||||
<table class="form-table" role="presentation">
|
||||
<tr>
|
||||
<th scope="row"><label for="groq-ai-max-output"><?php esc_html_e( 'Maximale output tokens', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<input type="number" id="groq-ai-max-output" name="<?php echo esc_attr( $option_key ); ?>[max_output_tokens]" value="<?php echo esc_attr( isset( $settings['max_output_tokens'] ) ? (int) $settings['max_output_tokens'] : 2048 ); ?>" min="128" max="8192" />
|
||||
<p class="description"><?php esc_html_e( 'Limitering van het aantal tokens per output voor compatibiliteit met verschillende modellen.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="groq-ai-term-bottom-meta"><?php esc_html_e( 'Term meta key (onderste tekst)', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<input type="text" id="groq-ai-term-bottom-meta" class="regular-text" name="<?php echo esc_attr( $option_key ); ?>[term_bottom_description_meta_key]" value="<?php echo esc_attr( isset( $settings['term_bottom_description_meta_key'] ) ? $settings['term_bottom_description_meta_key'] : '' ); ?>" />
|
||||
<p class="description"><?php esc_html_e( 'Optioneel: overschrijf in welke term meta key de onderste omschrijving moet landen.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Response format fallback', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td>
|
||||
<?php $this->render_response_format_compat_field(); ?>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><?php esc_html_e( 'Google Search Console & Analytics', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h2>
|
||||
<table class="form-table" role="presentation">
|
||||
<tr>
|
||||
<th scope="row"><label for="groq-ai-google-client-id"><?php esc_html_e( 'Google OAuth client ID', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<input type="text" id="groq-ai-google-client-id" class="regular-text" name="<?php echo esc_attr( $option_key ); ?>[google_oauth_client_id]" value="<?php echo esc_attr( isset( $settings['google_oauth_client_id'] ) ? $settings['google_oauth_client_id'] : '' ); ?>" autocomplete="off" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="groq-ai-google-client-secret"><?php esc_html_e( 'Google OAuth client secret', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<input type="password" id="groq-ai-google-client-secret" class="regular-text" name="<?php echo esc_attr( $option_key ); ?>[google_oauth_client_secret]" value="<?php echo esc_attr( isset( $settings['google_oauth_client_secret'] ) ? $settings['google_oauth_client_secret'] : '' ); ?>" autocomplete="off" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Search Console koppeling', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="<?php echo esc_attr( $option_key ); ?>[google_enable_gsc]" value="1" <?php checked( ! empty( $settings['google_enable_gsc'] ) ); ?> />
|
||||
<?php esc_html_e( 'Search Console data gebruiken in term prompts', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?>
|
||||
</label>
|
||||
<p>
|
||||
<input type="url" class="regular-text" name="<?php echo esc_attr( $option_key ); ?>[google_gsc_site_url]" value="<?php echo esc_attr( isset( $settings['google_gsc_site_url'] ) ? $settings['google_gsc_site_url'] : '' ); ?>" placeholder="sc-domain:voorbeeld.nl" />
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Analytics koppeling', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="<?php echo esc_attr( $option_key ); ?>[google_enable_ga]" value="1" <?php checked( ! empty( $settings['google_enable_ga'] ) ); ?> />
|
||||
<?php esc_html_e( 'GA4 data meesturen (landing page statistieken)', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?>
|
||||
</label>
|
||||
<p>
|
||||
<input type="text" class="regular-text" name="<?php echo esc_attr( $option_key ); ?>[google_ga4_property_id]" value="<?php echo esc_attr( isset( $settings['google_ga4_property_id'] ) ? $settings['google_ga4_property_id'] : '' ); ?>" placeholder="properties/123456789" />
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="submit"><?php submit_button( __( 'Instellingen opslaan', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'primary', 'submit', false ); ?></p>
|
||||
</form>
|
||||
<div style="margin-top:24px; padding:16px; border:1px solid #dcdcde; background:#fff;">
|
||||
<h2><?php esc_html_e( 'Google verbinding', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h2>
|
||||
<p>
|
||||
<?php
|
||||
if ( $google_connected ) {
|
||||
$timestamp = $google_connected_at ? date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $google_connected_at ) : '';
|
||||
printf(
|
||||
esc_html__( 'Verbonden als %1$s%2$s.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
|
||||
$google_connected_email ? esc_html( $google_connected_email ) : esc_html__( 'onbekende gebruiker', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
|
||||
$timestamp ? ' — ' . esc_html( $timestamp ) : ''
|
||||
);
|
||||
} else {
|
||||
esc_html_e( 'Nog niet gekoppeld aan Google OAuth.', GROQ_AI_PRODUCT_TEXT_DOMAIN );
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
<div style="display:flex; flex-wrap:wrap; gap:12px; align-items:center;">
|
||||
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
|
||||
<?php wp_nonce_field( 'groq_ai_google_oauth', '_wpnonce' ); ?>
|
||||
<input type="hidden" name="action" value="groq_ai_google_oauth_start" />
|
||||
<input type="hidden" name="redirect_to" value="<?php echo esc_url( $current_page ); ?>" />
|
||||
<button type="submit" class="button button-primary"><?php echo $google_connected ? esc_html__( 'Opnieuw verbinden', GROQ_AI_PRODUCT_TEXT_DOMAIN ) : esc_html__( 'Verbind met Google', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></button>
|
||||
</form>
|
||||
<?php if ( $google_connected ) : ?>
|
||||
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
|
||||
<?php wp_nonce_field( 'groq_ai_google_disconnect', '_wpnonce' ); ?>
|
||||
<input type="hidden" name="action" value="groq_ai_google_oauth_disconnect" />
|
||||
<input type="hidden" name="redirect_to" value="<?php echo esc_url( $current_page ); ?>" />
|
||||
<button type="submit" class="button"><?php esc_html_e( 'Ontkoppelen', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></button>
|
||||
</form>
|
||||
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
|
||||
<?php wp_nonce_field( 'groq_ai_google_test_connection', '_wpnonce' ); ?>
|
||||
<input type="hidden" name="action" value="groq_ai_google_test_connection" />
|
||||
<input type="hidden" name="redirect_to" value="<?php echo esc_url( $current_page ); ?>" />
|
||||
<button type="submit" class="button button-secondary"><?php esc_html_e( 'Verbinding testen', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +411,8 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
#adminmenu a[href="options-general.php?page=groq-ai-product-text-modules"],
|
||||
#adminmenu a[href="options-general.php?page=groq-ai-product-text-logs"],
|
||||
#adminmenu a[href="options-general.php?page=groq-ai-product-text-prompts"],
|
||||
#adminmenu a[href="options-general.php?page=groq-ai-product-text-term"] {
|
||||
#adminmenu a[href="options-general.php?page=groq-ai-product-text-term"],
|
||||
#adminmenu a[href="options-general.php?page=groq-ai-product-text-log"] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -425,6 +731,239 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_modules_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$option_key = $this->plugin->get_option_key();
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e( 'Siti AI modules', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||
<p class="description"><?php esc_html_e( 'Schakel aanvullende integraties in en bepaal grenzen voor gegenereerde content.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||
<?php settings_errors( $option_key ); ?>
|
||||
<form method="post" action="options.php">
|
||||
<?php settings_fields( $option_key ); ?>
|
||||
<table class="form-table" role="presentation">
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Rank Math integratie', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td><?php $this->render_rankmath_module_field(); ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php submit_button( __( 'Modules opslaan', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_logs_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$logs_table = new Groq_AI_Logs_Table( $this->plugin );
|
||||
$logs_table->prepare_items();
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e( 'AI-logboek', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||
<form method="get">
|
||||
<input type="hidden" name="page" value="groq-ai-product-text-logs" />
|
||||
<?php $logs_table->search_box( __( 'Zoek logboek', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'groq-ai-logs' ); ?>
|
||||
<?php $logs_table->display(); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_log_detail_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$log_id = isset( $_GET['log_id'] ) ? absint( $_GET['log_id'] ) : 0;
|
||||
$back_url = $this->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 );
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e( 'Logdetail', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||
<p>
|
||||
<a href="<?php echo esc_url( $back_url ); ?>" class="button">← <?php esc_html_e( 'Terug naar logboek', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></a>
|
||||
</p>
|
||||
<?php if ( ! $log ) : ?>
|
||||
<p><?php esc_html_e( 'Log niet gevonden of verwijderd.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||
<?php else : ?>
|
||||
<table class="widefat striped" style="margin-top:16px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Datum', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td><?php echo esc_html( mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $log['created_at'] ) ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Gebruiker', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td>
|
||||
<?php
|
||||
if ( $log['user_id'] ) {
|
||||
$user = get_userdata( $log['user_id'] );
|
||||
echo $user ? esc_html( $user->display_name ) : esc_html( (string) $log['user_id'] );
|
||||
} else {
|
||||
echo '—';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Product', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td>
|
||||
<?php
|
||||
if ( $log['post_id'] ) {
|
||||
$link = get_edit_post_link( $log['post_id'] );
|
||||
$title = $log['post_title'] ? $log['post_title'] : sprintf( __( 'Product #%d', GROQ_AI_PRODUCT_TEXT_DOMAIN ), (int) $log['post_id'] );
|
||||
echo $link ? '<a href="' . esc_url( $link ) . '">' . esc_html( $title ) . '</a>' : esc_html( $title );
|
||||
} else {
|
||||
echo '—';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Provider', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td><?php echo esc_html( $log['provider'] ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Model', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td><?php echo esc_html( $log['model'] ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Status', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td><?php echo esc_html( $log['status'] ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Tokens', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td>
|
||||
<?php
|
||||
printf(
|
||||
esc_html__( 'Prompt: %1$s — Completion: %2$s — Totaal: %3$s', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
|
||||
isset( $log['tokens_prompt'] ) ? number_format_i18n( (int) $log['tokens_prompt'] ) : '—',
|
||||
isset( $log['tokens_completion'] ) ? number_format_i18n( (int) $log['tokens_completion'] ) : '—',
|
||||
isset( $log['tokens_total'] ) ? number_format_i18n( (int) $log['tokens_total'] ) : '—'
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Foutmelding', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td><?php echo $log['error_message'] ? esc_html( $log['error_message'] ) : '—'; ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2><?php esc_html_e( 'Prompt', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h2>
|
||||
<pre style="background:#fff;border:1px solid #dcdcde;padding:12px;white-space:pre-wrap;"><?php echo esc_html( $log['prompt'] ); ?></pre>
|
||||
|
||||
<h2><?php esc_html_e( 'AI-respons', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h2>
|
||||
<pre style="background:#f9f9f9;border:1px solid #dcdcde;padding:12px;white-space:pre-wrap;"><?php echo esc_html( $log['response'] ); ?></pre>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_prompt_settings_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$option_key = $this->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 );
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e( 'Prompt & context', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||
<p class="description"><?php esc_html_e( 'Bepaal welke winkelcontext standaard meegestuurd wordt en hoe de prompt eruit ziet voor nieuwe generaties.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||
<div style="margin:16px 0; padding:16px; background:#fff; border:1px solid #dcdcde;">
|
||||
<strong><?php esc_html_e( 'Voorbeeldprompt', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></strong>
|
||||
<pre style="background:#f6f7f7; padding:12px; border-radius:4px; white-space:pre-wrap; overflow:auto; margin-top:8px;"><?php echo esc_html( $preview ); ?></pre>
|
||||
</div>
|
||||
<?php settings_errors( $option_key ); ?>
|
||||
<form method="post" action="options.php">
|
||||
<?php settings_fields( $option_key ); ?>
|
||||
<table class="form-table" role="presentation">
|
||||
<tr>
|
||||
<th scope="row"><label for="groq-ai-store-context"><?php esc_html_e( 'Winkelcontext', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<textarea id="groq-ai-store-context" class="large-text" rows="4" name="<?php echo esc_attr( $option_key ); ?>[store_context]"><?php echo esc_textarea( isset( $settings['store_context'] ) ? $settings['store_context'] : '' ); ?></textarea>
|
||||
<p class="description"><?php esc_html_e( 'Beschrijf je winkel, tone-of-voice en doelgroep. Wordt in system prompts gebruikt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="groq-ai-default-prompt"><?php esc_html_e( 'Standaard prompt', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<textarea id="groq-ai-default-prompt" class="large-text" rows="6" name="<?php echo esc_attr( $option_key ); ?>[default_prompt]"><?php echo esc_textarea( isset( $settings['default_prompt'] ) ? $settings['default_prompt'] : '' ); ?></textarea>
|
||||
<p class="description"><?php esc_html_e( 'Vult automatisch de AI-modal en termgenerator. Je kunt dit per product/term overschrijven.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Contextvelden', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td>
|
||||
<div class="groq-ai-context-defaults">
|
||||
<?php foreach ( $definitions as $key => $info ) :
|
||||
$checked = ! empty( $context_vals[ $key ] );
|
||||
$label = isset( $info['label'] ) ? $info['label'] : $key;
|
||||
$description = isset( $info['description'] ) ? $info['description'] : '';
|
||||
?>
|
||||
<label>
|
||||
<input type="checkbox" name="<?php echo esc_attr( $option_key ); ?>[context_fields][<?php echo esc_attr( $key ); ?>]" value="1" <?php checked( $checked ); ?> />
|
||||
<strong><?php echo esc_html( $label ); ?></strong>
|
||||
</label>
|
||||
<?php if ( $description ) : ?>
|
||||
<p class="description" style="margin:0 0 8px 24px;"><?php echo esc_html( $description ); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Productattributen', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||
<td><?php $this->render_product_attribute_includes_field(); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="groq-ai-image-mode"><?php esc_html_e( 'Afbeeldingen als context', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||
<td>
|
||||
<select id="groq-ai-image-mode" name="<?php echo esc_attr( $option_key ); ?>[image_context_mode]">
|
||||
<option value="none" <?php selected( 'none', $image_mode ); ?>><?php esc_html_e( 'Niet meesturen', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></option>
|
||||
<option value="url" <?php selected( 'url', $image_mode ); ?>><?php esc_html_e( 'Alleen URL en korte beschrijving', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></option>
|
||||
<option value="base64" <?php selected( 'base64', $image_mode ); ?>><?php esc_html_e( 'Inline base64 (alleen voor modellen die dit vereisen)', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></option>
|
||||
</select>
|
||||
<p class="description"><?php esc_html_e( 'Sommige modellen (zoals Gemini) ondersteunen beeldcontext; let op je tokenverbruik.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||
<label>
|
||||
<?php esc_html_e( 'Maximaal aantal afbeeldingen', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?>
|
||||
<input type="number" min="1" max="10" name="<?php echo esc_attr( $option_key ); ?>[image_context_limit]" value="<?php echo esc_attr( $image_limit ); ?>" style="width:80px;" />
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php submit_button( __( 'Prompt instellingen opslaan', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_term_generator_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
@@ -454,6 +993,20 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
return;
|
||||
}
|
||||
|
||||
$term_notice = isset( $_GET['groq_ai_term_notice'] ) ? sanitize_key( wp_unslash( $_GET['groq_ai_term_notice'] ) ) : '';
|
||||
$term_notice_status = isset( $_GET['groq_ai_term_status'] ) ? sanitize_key( wp_unslash( $_GET['groq_ai_term_status'] ) ) : 'success';
|
||||
$term_notice_message = '';
|
||||
if ( isset( $_GET['groq_ai_term_notice_message'] ) ) {
|
||||
$term_notice_message = sanitize_text_field( rawurldecode( wp_unslash( $_GET['groq_ai_term_notice_message'] ) ) );
|
||||
}
|
||||
if ( $term_notice && '' === $term_notice_message ) {
|
||||
if ( 'saved' === $term_notice ) {
|
||||
$term_notice_message = __( 'Term succesvol opgeslagen.', GROQ_AI_PRODUCT_TEXT_DOMAIN );
|
||||
} else {
|
||||
$term_notice_message = __( 'Actie voltooid.', GROQ_AI_PRODUCT_TEXT_DOMAIN );
|
||||
}
|
||||
}
|
||||
|
||||
$term_label = ( 'product_cat' === $taxonomy ) ? __( 'Categorie', GROQ_AI_PRODUCT_TEXT_DOMAIN ) : __( 'Term', GROQ_AI_PRODUCT_TEXT_DOMAIN );
|
||||
$word_count = $this->count_words( $term->description );
|
||||
$meta_prompt = get_term_meta( $term_id, 'groq_ai_term_custom_prompt', true );
|
||||
@@ -478,6 +1031,12 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
<h1>
|
||||
<?php echo esc_html( $term_label ); ?>: <?php echo esc_html( $term->name ); ?>
|
||||
</h1>
|
||||
<?php if ( $term_notice ) : ?>
|
||||
<?php $notice_class = ( 'error' === $term_notice_status ) ? 'notice notice-error' : 'notice notice-success'; ?>
|
||||
<div class="<?php echo esc_attr( $notice_class ); ?>">
|
||||
<p><?php echo esc_html( $term_notice_message ); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
@@ -648,6 +1207,8 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
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'] )
|
||||
@@ -815,14 +1376,25 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
}
|
||||
|
||||
public function enqueue_settings_assets( $hook ) {
|
||||
if ( ! in_array( $hook, [
|
||||
$allowed_hooks = [
|
||||
'settings_page_groq-ai-product-text',
|
||||
'settings_page_groq-ai-product-text-modules',
|
||||
'settings_page_groq-ai-product-text-prompts',
|
||||
'settings_page_groq-ai-product-text-categories',
|
||||
'settings_page_groq-ai-product-text-brands',
|
||||
'settings_page_groq-ai-product-text-term',
|
||||
], true ) ) {
|
||||
'settings_page_groq-ai-product-text-logs',
|
||||
];
|
||||
|
||||
$matches_hook = false;
|
||||
foreach ( $allowed_hooks as $allowed ) {
|
||||
if ( 0 === strpos( (string) $hook, $allowed ) ) {
|
||||
$matches_hook = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $matches_hook ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -848,7 +1420,9 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
true
|
||||
);
|
||||
|
||||
if ( 'settings_page_groq-ai-product-text-term' === $hook ) {
|
||||
$current_page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
|
||||
|
||||
if ( 0 === strpos( (string) $hook, 'settings_page_groq-ai-product-text-term' ) ) {
|
||||
wp_enqueue_script(
|
||||
'groq-ai-term-admin',
|
||||
plugins_url( 'assets/js/term-admin.js', GROQ_AI_PRODUCT_TEXT_FILE ),
|
||||
@@ -875,7 +1449,7 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
$bulk_allow_regen = false;
|
||||
$bulk_strings = [];
|
||||
|
||||
if ( 'settings_page_groq-ai-product-text-categories' === $hook ) {
|
||||
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 ),
|
||||
@@ -887,7 +1461,7 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
'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 ) {
|
||||
} 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;
|
||||
@@ -957,4 +1531,300 @@ class Groq_AI_Product_Text_Settings_Page {
|
||||
|
||||
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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user