diff --git a/assets/js/term-admin.js b/assets/js/term-admin.js index fa0715c..ee4bfa8 100644 --- a/assets/js/term-admin.js +++ b/assets/js/term-admin.js @@ -9,7 +9,11 @@ } const promptField = document.getElementById('groq-ai-term-prompt'); - const outputField = document.getElementById('groq-ai-term-generated'); + const outputTopField = document.getElementById('groq-ai-term-generated-top'); + const outputBottomField = document.getElementById('groq-ai-term-generated-bottom'); + const outputMetaTitleField = document.getElementById('groq-ai-term-generated-meta-title'); + const outputMetaDescriptionField = document.getElementById('groq-ai-term-generated-meta-description'); + const outputFocusKeywordsField = document.getElementById('groq-ai-term-generated-focus-keywords'); const rawField = document.getElementById('groq-ai-term-raw'); const statusField = document.getElementById('groq-ai-term-status'); const applyButton = document.getElementById('groq-ai-term-apply'); @@ -48,14 +52,27 @@ applyButton.addEventListener('click', () => { const descriptionField = document.getElementById('description'); const bottomDescriptionField = document.getElementById('groq-ai-term-bottom-description'); - if (!outputField) { + const rankmathTitleField = document.getElementById('groq-ai-rankmath-title'); + const rankmathDescriptionField = document.getElementById('groq-ai-rankmath-description'); + const rankmathKeywordsField = document.getElementById('groq-ai-rankmath-keywords'); + if (!outputTopField) { return; } - if (bottomDescriptionField) { - bottomDescriptionField.value = outputField.value || ''; - } else if (descriptionField) { - descriptionField.value = outputField.value || ''; + if (descriptionField) { + descriptionField.value = outputTopField.value || ''; + } + if (bottomDescriptionField && outputBottomField) { + bottomDescriptionField.value = outputBottomField.value || ''; + } + if (rankmathTitleField && outputMetaTitleField) { + rankmathTitleField.value = outputMetaTitleField.value || ''; + } + if (rankmathDescriptionField && outputMetaDescriptionField) { + rankmathDescriptionField.value = outputMetaDescriptionField.value || ''; + } + if (rankmathKeywordsField && outputFocusKeywordsField) { + rankmathKeywordsField.value = outputFocusKeywordsField.value || ''; } setStatus('Tekst ingevuld. Vergeet niet op "Opslaan" te klikken.', 'success'); @@ -76,9 +93,11 @@ rawField.textContent = ''; } - if (outputField) { - outputField.value = ''; - } + if (outputTopField) outputTopField.value = ''; + if (outputBottomField) outputBottomField.value = ''; + if (outputMetaTitleField) outputMetaTitleField.value = ''; + if (outputMetaDescriptionField) outputMetaDescriptionField.value = ''; + if (outputFocusKeywordsField) outputFocusKeywordsField.value = ''; fetch(GroqAITermGenerator.ajaxUrl, { method: 'POST', @@ -94,9 +113,25 @@ throw new Error(errorMessage); } - if (outputField) { - const text = json.data && json.data.description ? json.data.description : ''; - outputField.value = String(text).trim(); + if (outputTopField) { + const top = json.data && (json.data.top_description || json.data.description) ? (json.data.top_description || json.data.description) : ''; + outputTopField.value = String(top).trim(); + } + if (outputBottomField) { + const bottom = json.data && json.data.bottom_description ? json.data.bottom_description : ''; + outputBottomField.value = String(bottom).trim(); + } + if (outputMetaTitleField) { + const metaTitle = json.data && json.data.meta_title ? json.data.meta_title : ''; + outputMetaTitleField.value = String(metaTitle).trim(); + } + if (outputMetaDescriptionField) { + const metaDescription = json.data && json.data.meta_description ? json.data.meta_description : ''; + outputMetaDescriptionField.value = String(metaDescription).trim(); + } + if (outputFocusKeywordsField) { + const keywords = json.data && json.data.focus_keywords ? json.data.focus_keywords : ''; + outputFocusKeywordsField.value = String(keywords).trim(); } if (rawField) { rawField.textContent = (json.data && json.data.raw ? String(json.data.raw) : '').trim(); diff --git a/groq-ai-product-text.php b/groq-ai-product-text.php index 6a43767..753d3b3 100644 --- a/groq-ai-product-text.php +++ b/groq-ai-product-text.php @@ -2,7 +2,7 @@ /** * Plugin Name: SitiAI Product Teksten * Description: Genereer productteksten met diverse AI-aanbieders rechtstreeks vanuit WooCommerce. - * Version: 1.4.3 + * Version: 1.4.4 * Author: SitiAI * Text Domain: siti-ai-product-content-generator * Domain Path: /languages diff --git a/includes/Admin/class-groq-ai-settings-page.php b/includes/Admin/class-groq-ai-settings-page.php index 48bf588..b9bc0d7 100644 --- a/includes/Admin/class-groq-ai-settings-page.php +++ b/includes/Admin/class-groq-ai-settings-page.php @@ -321,9 +321,18 @@ class Groq_AI_Product_Text_Settings_Page { $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 ); - $bottom_description = ''; - if ( '' !== $bottom_meta_key ) { - $bottom_description = (string) get_term_meta( $term_id, $bottom_meta_key, true ); + $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 = (string) $meta_prompt; if ( '' === trim( $default_prompt ) ) { @@ -360,23 +369,24 @@ class Groq_AI_Product_Text_Settings_Page {

- - - - - -

- -

- - - + + + + +

+ +

+ + @@ -384,6 +394,34 @@ class Groq_AI_Product_Text_Settings_Page {

+ + + + + +

+ + + + + + + + + + + + + + + + + + + + + + @@ -407,19 +445,21 @@ class Groq_AI_Product_Text_Settings_Page {

- +

+

+

- + + +

+ +

+ +

+ +


 			
@@ -438,6 +478,24 @@ class Groq_AI_Product_Text_Settings_Page {
 		return $key;
 	}
 
+	private function resolve_rankmath_term_meta_keys( $term, $settings ) {
+		$keys = [
+			'title'        => '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',
+		];
+	}
+
 	public function handle_save_term_content() {
 		if ( ! current_user_can( 'manage_options' ) ) {
 			wp_die( esc_html__( 'Geen toestemming.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) );
@@ -450,6 +508,9 @@ class Groq_AI_Product_Text_Settings_Page {
 		$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'] ) ) : '';
+		$rankmath_meta_title = isset( $_POST['groq_ai_rankmath_meta_title'] ) ? sanitize_text_field( wp_unslash( $_POST['groq_ai_rankmath_meta_title'] ) ) : '';
+		$rankmath_meta_description = isset( $_POST['groq_ai_rankmath_meta_description'] ) ? sanitize_text_field( wp_unslash( $_POST['groq_ai_rankmath_meta_description'] ) ) : '';
+		$rankmath_focus_keywords = isset( $_POST['groq_ai_rankmath_focus_keywords'] ) ? sanitize_text_field( wp_unslash( $_POST['groq_ai_rankmath_focus_keywords'] ) ) : '';
 
 		if ( '' === $taxonomy || ! taxonomy_exists( $taxonomy ) || ! $term_id ) {
 			wp_safe_redirect( $this->get_settings_page_url() );
@@ -470,8 +531,15 @@ class Groq_AI_Product_Text_Settings_Page {
 			$term = get_term( $term_id, $taxonomy );
 			if ( $term && ! is_wp_error( $term ) ) {
 				$bottom_meta_key = $this->resolve_term_bottom_description_meta_key( $term, $settings );
-				if ( '' !== $bottom_meta_key ) {
-					update_term_meta( $term_id, $bottom_meta_key, $bottom_description );
+				$effective_bottom_meta_key = '' !== $bottom_meta_key ? $bottom_meta_key : 'groq_ai_term_bottom_description';
+				update_term_meta( $term_id, $effective_bottom_meta_key, $bottom_description );
+
+				$rankmath_module_enabled = $this->plugin->is_module_enabled( 'rankmath', $settings );
+				if ( $rankmath_module_enabled ) {
+					$rankmath_keys = $this->resolve_rankmath_term_meta_keys( $term, $settings );
+					update_term_meta( $term_id, $rankmath_keys['title'], $rankmath_meta_title );
+					update_term_meta( $term_id, $rankmath_keys['description'], $rankmath_meta_description );
+					update_term_meta( $term_id, $rankmath_keys['focus_keyword'], $rankmath_focus_keywords );
 				}
 			}
 		}
diff --git a/includes/Core/class-groq-ai-ajax-controller.php b/includes/Core/class-groq-ai-ajax-controller.php
index e559a30..bb828b3 100644
--- a/includes/Core/class-groq-ai-ajax-controller.php
+++ b/includes/Core/class-groq-ai-ajax-controller.php
@@ -105,20 +105,14 @@ class Groq_AI_Ajax_Controller {
 			];
 		}
 
-		$default_bottom_key = isset( $settings['term_bottom_description_meta_key'] ) ? sanitize_key( (string) $settings['term_bottom_description_meta_key'] ) : '';
-		$bottom_meta_key    = apply_filters( 'groq_ai_term_bottom_description_meta_key', $default_bottom_key, $term, $settings );
-		$bottom_meta_key    = sanitize_key( (string) $bottom_meta_key );
-		$has_bottom_field   = ( '' !== $bottom_meta_key );
-
-		$top_description = isset( $parsed['description'] ) ? (string) $parsed['description'] : '';
-		$bottom_description = isset( $parsed['bottom_description'] ) ? (string) $parsed['bottom_description'] : '';
-		$apply_text = $has_bottom_field ? ( '' !== $bottom_description ? $bottom_description : $top_description ) : $top_description;
-
 		wp_send_json_success(
 			[
-				'top_description' => $top_description,
-				'bottom_description' => $has_bottom_field ? $apply_text : $bottom_description,
-				'description' => $apply_text,
+				'top_description' => isset( $parsed['top_description'] ) ? $parsed['top_description'] : ( isset( $parsed['description'] ) ? $parsed['description'] : '' ),
+				'bottom_description' => isset( $parsed['bottom_description'] ) ? $parsed['bottom_description'] : '',
+				'meta_title' => isset( $parsed['meta_title'] ) ? $parsed['meta_title'] : '',
+				'meta_description' => isset( $parsed['meta_description'] ) ? $parsed['meta_description'] : '',
+				'focus_keywords' => isset( $parsed['focus_keywords'] ) ? $parsed['focus_keywords'] : '',
+				'description' => isset( $parsed['description'] ) ? $parsed['description'] : ( isset( $parsed['top_description'] ) ? $parsed['top_description'] : '' ),
 				'raw' => $response_text,
 			]
 		);
diff --git a/includes/Services/Prompt/class-groq-ai-prompt-builder.php b/includes/Services/Prompt/class-groq-ai-prompt-builder.php
index 7263af4..1eae783 100644
--- a/includes/Services/Prompt/class-groq-ai-prompt-builder.php
+++ b/includes/Services/Prompt/class-groq-ai-prompt-builder.php
@@ -425,6 +425,7 @@ class Groq_AI_Prompt_Builder {
 		}
 
 		$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';
 		if ( '' !== $bottom_meta_key && $term_id ) {
 			$bottom = (string) get_term_meta( $term_id, $bottom_meta_key, true );
 			$bottom = trim( wp_strip_all_tags( $bottom ) );
@@ -474,27 +475,18 @@ class Groq_AI_Prompt_Builder {
 		$title_pixels     = $this->settings_manager->get_rankmath_meta_title_pixel_limit( $settings );
 		$desc_pixels      = $this->settings_manager->get_rankmath_meta_description_pixel_limit( $settings );
 
-		$bottom_meta_key = $this->resolve_term_bottom_description_meta_key( null, $settings );
-		$use_bottom_field = ( '' !== $bottom_meta_key );
-
-		$properties = [];
-		$required   = [];
-
-		if ( $use_bottom_field ) {
-			$properties['bottom_description'] = [
+		$properties = [
+			'top_description' => [
 				'type'        => 'string',
-				'description' => __( 'Uitgebreide HTML-omschrijving (helemaal onderaan) met paragrafen en eventueel lijstjes.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
+				'description' => __( 'Korte HTML-omschrijving (1 alinea) voor de standaard WordPress term description. Exact één alinea in 

-tags.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'minLength' => 20, - ]; - $required[] = 'bottom_description'; - } else { - $properties['description'] = [ + ], + 'bottom_description' => [ 'type' => 'string', - 'description' => __( 'HTML-omschrijving (WordPress term description) met paragrafen en eventueel lijstjes.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'description' => __( 'Uitgebreide HTML-omschrijving (helemaal onderaan), 2–4 alinea’s, met paragrafen en eventueel lijstjes.', GROQ_AI_PRODUCT_TEXT_DOMAIN ), 'minLength' => 20, - ]; - $required[] = 'description'; - } + ], + ]; if ( $rankmath_enabled ) { $properties['meta_title'] = [ @@ -529,7 +521,7 @@ class Groq_AI_Prompt_Builder { $schema = [ 'type' => 'object', 'properties' => $properties, - 'required' => $required, + 'required' => [ 'top_description', 'bottom_description' ], 'additionalProperties' => false, ]; @@ -572,36 +564,24 @@ class Groq_AI_Prompt_Builder { ]; } - $bottom_meta_key = $this->resolve_term_bottom_description_meta_key( null, $settings ); - $use_bottom_field = ( '' !== $bottom_meta_key ); - - $description = isset( $decoded['description'] ) ? trim( (string) $decoded['description'] ) : ''; $top = isset( $decoded['top_description'] ) ? trim( (string) $decoded['top_description'] ) : ''; $bottom = isset( $decoded['bottom_description'] ) ? trim( (string) $decoded['bottom_description'] ) : ''; - - if ( $use_bottom_field ) { - if ( '' === $bottom ) { - $bottom = '' !== $description ? $description : $top; - } - if ( '' === $bottom ) { - return new WP_Error( 'groq_ai_parse_error', __( 'De AI-respons bevatte geen bottom_description veld.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ); - } - } else { - if ( '' === $description ) { - $description = '' !== $top ? $top : $bottom; - } - if ( '' === $description ) { - return new WP_Error( 'groq_ai_parse_error', __( 'De AI-respons bevatte geen description veld.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ); - } + // Backward compatibility: older prompts only returned `description`. + if ( '' === $top && isset( $decoded['description'] ) ) { + $top = trim( (string) $decoded['description'] ); + } + if ( '' === $top && '' === $bottom ) { + return new WP_Error( 'groq_ai_parse_error', __( 'De AI-respons bevatte geen top_description/bottom_description velden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ); } $result = []; - if ( $use_bottom_field ) { - $result['bottom_description'] = $bottom; + if ( '' !== $top ) { + $result['top_description'] = $top; // For backwards compatibility with existing UI, keep `description` alias. - $result['description'] = $bottom; - } else { - $result['description'] = $description; + $result['description'] = $top; + } + if ( '' !== $bottom ) { + $result['bottom_description'] = $bottom; } if ( isset( $decoded['meta_title'] ) ) { @@ -628,15 +608,10 @@ class Groq_AI_Prompt_Builder { } private function get_term_structured_response_instructions( $settings = null ) { - $bottom_meta_key = $this->resolve_term_bottom_description_meta_key( null, $settings ); - $use_bottom_field = ( '' !== $bottom_meta_key ); - - $schema_parts = []; - if ( $use_bottom_field ) { - $schema_parts[] = '"bottom_description":"..."'; - } else { - $schema_parts[] = '"description":"..."'; - } + $schema_parts = [ + '"top_description":"..."', + '"bottom_description":"..."', + ]; $rankmath_enabled = $this->settings_manager->is_module_enabled( 'rankmath', $settings ); if ( $rankmath_enabled ) { @@ -652,13 +627,9 @@ class Groq_AI_Prompt_Builder { $json_structure ); - if ( $use_bottom_field ) { - $instruction .= ' ' . __( 'Zorg dat bottom_description geldige HTML bevat. Dit is de tekst die helemaal onderaan de pagina komt.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); - } else { - $instruction .= ' ' . __( 'Zorg dat description geldige HTML bevat.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); - } + $instruction .= ' ' . __( 'Zorg dat top_description en bottom_description geldige HTML bevatten. top_description moet exact één alinea zijn in

-tags. bottom_description moet 2–4 alinea’s bevatten.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); $instruction .= ' ' . __( 'Voeg geen extra tekst buiten het JSON-object toe.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); - $instruction .= ' ' . __( 'Als in de context een sectie "Interne links" staat, verwerk dan 2–5 van deze links natuurlijk in de hoofdtekst als HTML-links (Anker).', GROQ_AI_PRODUCT_TEXT_DOMAIN ); + $instruction .= ' ' . __( 'Als in de context een sectie "Interne links" staat, verwerk dan 2–5 van deze links natuurlijk in bottom_description als HTML-links (Anker).', GROQ_AI_PRODUCT_TEXT_DOMAIN ); return $instruction; }