From 985f7dfbcdded8116a54c9bd8afe015a399f4f15 Mon Sep 17 00:00:00 2001 From: Roberto Guagliardo Date: Fri, 19 Dec 2025 16:10:57 +0000 Subject: [PATCH] Refactor localization strings to use constant for text domain; add image context limit feature - Updated localization strings in various classes to use the constant `GROQ_AI_PRODUCT_TEXT_DOMAIN` instead of hardcoded text domain. - Introduced an image context limit setting in the settings manager and adjusted related methods to accommodate this new feature. - Modified prompt builder to handle image context limit when building product context blocks and retrieving image payloads. - Enhanced error handling and response structures to include new fields related to image context. - Added support for title suggestions in the structured response from the AI. --- assets/css/admin.css | 30 ++++ assets/js/admin.js | 93 +++++++++- docker-compose.yml | 2 +- groq-ai-product-text.php | 53 +++++- includes/Admin/class-groq-ai-logs-table.php | 18 +- includes/Admin/class-groq-ai-product-ui.php | 102 ++++++----- .../Admin/class-groq-ai-settings-page.php | 165 ++++++++++-------- .../Core/class-groq-ai-ajax-controller.php | 20 ++- ...class-groq-ai-abstract-openai-provider.php | 10 +- .../class-groq-ai-provider-google.php | 12 +- .../Providers/class-groq-ai-provider-groq.php | 2 +- .../class-groq-ai-provider-openai.php | 2 +- .../Prompt/class-groq-ai-prompt-builder.php | 136 ++++++++++++--- .../class-groq-ai-settings-manager.php | 48 +++-- languages/.gitkeep | 0 15 files changed, 504 insertions(+), 189 deletions(-) create mode 100644 languages/.gitkeep diff --git a/assets/css/admin.css b/assets/css/admin.css index e43e960..572ab15 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -123,6 +123,36 @@ width: 100%; } +.groq-ai-title-suggestions { + border: 1px dashed #dcdcde; + border-radius: 4px; + padding: 8px; + background: #fefefe; + margin-top: 8px; +} + +.groq-ai-title-suggestions__options { + display: flex; + flex-direction: column; + gap: 6px; + margin-top: 6px; +} + +.groq-ai-title-suggestions__option { + display: flex; + align-items: flex-start; + gap: 8px; +} + +.groq-ai-title-suggestions__option input[type='radio'] { + margin-top: 3px; +} + +.groq-ai-title-suggestions__hint { + margin-top: 6px; + margin-bottom: 0; +} + .groq-ai-modal__raw { margin-top: 16px; } diff --git a/assets/js/admin.js b/assets/js/admin.js index 275c11c..10b6313 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -28,6 +28,8 @@ rankMathAction: field.getAttribute('data-rankmath-action') || '', status: field.querySelector('.groq-ai-apply-status') || null, statusTimer: null, + suggestionWrapper: field.querySelector('[data-title-suggestions]') || null, + suggestionOptions: field.querySelector('[data-title-suggestions-options]') || null, }; }); @@ -92,8 +94,8 @@ statusField.setAttribute('data-status', type); } - const loadingText = window.wp && wp.i18n ? wp.i18n.__('AI is bezig met schrijven...', 'groq-ai-product-text') : 'AI is bezig met schrijven...'; - const retryText = window.wp && wp.i18n ? wp.i18n.__('Probeer het opnieuw of pas je prompt/context aan.', 'groq-ai-product-text') : 'Probeer het opnieuw of pas je prompt/context aan.'; + const loadingText = window.wp && wp.i18n ? wp.i18n.__('AI is bezig met schrijven...', 'siti-ai-product-content-generator') : 'AI is bezig met schrijven...'; + const retryText = window.wp && wp.i18n ? wp.i18n.__('Probeer het opnieuw of pas je prompt/context aan.', 'siti-ai-product-content-generator') : 'Probeer het opnieuw of pas je prompt/context aan.'; function toggleLoading(isLoading) { modal.classList.toggle('is-loading', isLoading); @@ -120,6 +122,7 @@ jsonCopyButton.disabled = true; } resetFieldStatuses(); + clearTitleSuggestions(); fetch(GroqAIGenerator.ajaxUrl, { method: 'POST', @@ -142,6 +145,7 @@ entry.textarea.value = fields[key] || ''; } }); + updateTitleSuggestions(fields.title_suggestions); resultField.textContent = (json.data.raw || '').trim(); resultWrapper.hidden = false; if (jsonCopyButton) { @@ -317,6 +321,8 @@ return ['textarea[name="rank_math_description"]']; case 'focus_keywords': return ['input[name="rank_math_focus_keyword"]']; + case 'slug': + return ['#post_name', 'input[name="post_name"]', '#new-post-slug']; default: return []; } @@ -357,6 +363,89 @@ }); } + function clearTitleSuggestions() { + const entry = resultFields.title; + if (!entry || !entry.suggestionWrapper || !entry.suggestionOptions) { + return; + } + entry.suggestionOptions.innerHTML = ''; + entry.suggestionWrapper.hidden = true; + } + + function updateTitleSuggestions(options) { + const entry = resultFields.title; + if (!entry || !entry.suggestionWrapper || !entry.suggestionOptions) { + return; + } + + entry.suggestionOptions.innerHTML = ''; + + const sanitized = Array.isArray(options) + ? options + .map((option) => (typeof option === 'string' ? option.trim() : '')) + .filter((option) => option.length > 0) + .slice(0, 3) + : []; + + if (!sanitized.length) { + entry.suggestionWrapper.hidden = true; + return; + } + + entry.suggestionWrapper.hidden = false; + + const currentValue = entry.textarea ? entry.textarea.value.trim() : ''; + const normalizedCurrent = currentValue.toLowerCase(); + let selectedValue = ''; + + if (normalizedCurrent) { + const matched = sanitized.find((text) => text.toLowerCase() === normalizedCurrent); + if (matched) { + selectedValue = matched; + if (entry.textarea) { + entry.textarea.value = matched; + } + } + } + + if (!selectedValue) { + selectedValue = sanitized[0]; + if (entry.textarea) { + entry.textarea.value = sanitized[0]; + } + } + + const groupName = `groq-ai-title-option-${Date.now()}`; + + sanitized.forEach((text, index) => { + const optionId = `${groupName}-${index}`; + const optionWrapper = document.createElement('label'); + optionWrapper.className = 'groq-ai-title-suggestions__option'; + + const radio = document.createElement('input'); + radio.type = 'radio'; + radio.name = groupName; + radio.id = optionId; + radio.value = text; + if (text === selectedValue) { + radio.checked = true; + } + + radio.addEventListener('change', () => { + if (entry.textarea) { + entry.textarea.value = text; + } + }); + + const textSpan = document.createElement('span'); + textSpan.textContent = text; + + optionWrapper.appendChild(radio); + optionWrapper.appendChild(textSpan); + entry.suggestionOptions.appendChild(optionWrapper); + }); + } + function resetContextToggles() { const defaults = GroqAIGenerator.contextDefaults || {}; contextToggles.forEach((toggle) => { diff --git a/docker-compose.yml b/docker-compose.yml index f590dd9..c48ffc7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress WORDPRESS_DB_NAME: wordpress - WORDPRESS_DEBUG: 1 + WORDPRESS_DEBUG: 0 WP_ENVIRONMENT_TYPE: local volumes: - wordpress_data:/var/www/html diff --git a/groq-ai-product-text.php b/groq-ai-product-text.php index 8f8b014..8332188 100644 --- a/groq-ai-product-text.php +++ b/groq-ai-product-text.php @@ -2,8 +2,10 @@ /** * Plugin Name: SitiAI Product Teksten * Description: Genereer productteksten met diverse AI-aanbieders rechtstreeks vanuit WooCommerce. - * Version: 1.2.2 + * Version: 1.3.0 * Author: SitiAI + * Text Domain: siti-ai-product-content-generator + * Domain Path: /languages */ if ( ! defined( 'ABSPATH' ) ) { @@ -27,6 +29,14 @@ if ( ! defined( 'GROQ_AI_PRODUCT_TEXT_VERSION' ) ) { define( 'GROQ_AI_PRODUCT_TEXT_VERSION', $groq_ai_version ); } +if ( ! defined( 'GROQ_AI_PRODUCT_TEXT_DOMAIN' ) ) { + define( 'GROQ_AI_PRODUCT_TEXT_DOMAIN', 'siti-ai-product-content-generator' ); +} + +if ( ! defined( 'GROQ_AI_PRODUCT_TEXT_LEGACY_DOMAIN' ) ) { + define( 'GROQ_AI_PRODUCT_TEXT_LEGACY_DOMAIN', 'groq-ai-product-text' ); +} + if ( ! defined( 'GROQ_AI_DEBUG_TRACE_ADDED' ) && defined( 'WP_DEBUG' ) && WP_DEBUG ) { define( 'GROQ_AI_DEBUG_TRACE_ADDED', true ); } @@ -63,6 +73,9 @@ final class Groq_AI_Product_Text_Plugin { const CONVERSATION_OPTION_KEY = 'groq_ai_product_text_conversations'; const MODELS_CACHE_OPTION_KEY = 'groq_ai_product_text_models'; + /** @var bool */ + private $textdomain_loaded = false; + private static $instance = null; /** @var Groq_AI_Service_Container */ @@ -91,10 +104,36 @@ final class Groq_AI_Product_Text_Plugin { $this->settings_page = new Groq_AI_Product_Text_Settings_Page( $this, $this->get_provider_manager() ); $this->product_ui = new Groq_AI_Product_Text_Product_UI( $this ); + add_action( 'plugins_loaded', [ $this, 'maybe_load_textdomain_early' ], 0 ); + add_action( 'init', [ $this, 'load_textdomain' ] ); add_action( 'plugins_loaded', [ $this, 'maybe_create_logs_table' ] ); add_action( 'load-plugins.php', [ $this, 'maybe_deactivate_if_woocommerce_missing' ] ); } + public function load_textdomain() { + if ( $this->textdomain_loaded ) { + return; + } + + $relative_path = dirname( plugin_basename( GROQ_AI_PRODUCT_TEXT_FILE ) ) . '/languages'; + + load_plugin_textdomain( GROQ_AI_PRODUCT_TEXT_DOMAIN, false, $relative_path ); + + if ( defined( 'GROQ_AI_PRODUCT_TEXT_LEGACY_DOMAIN' ) && GROQ_AI_PRODUCT_TEXT_LEGACY_DOMAIN !== GROQ_AI_PRODUCT_TEXT_DOMAIN ) { + load_plugin_textdomain( GROQ_AI_PRODUCT_TEXT_LEGACY_DOMAIN, false, $relative_path ); + } + + $this->textdomain_loaded = true; + } + + public function maybe_load_textdomain_early() { + if ( did_action( 'init' ) ) { + return; + } + + $this->load_textdomain(); + } + private function register_services() { $this->container = new Groq_AI_Service_Container(); @@ -198,7 +237,7 @@ final class Groq_AI_Product_Text_Plugin { ?>

- +

get_settings_manager()->get_image_context_mode( $settings ); } + public function get_image_context_limit( $settings = null ) { + return $this->get_settings_manager()->get_image_context_limit( $settings ); + } + public function should_use_response_format( Groq_AI_Provider_Interface $provider, $settings ) { return ! $this->is_response_format_compat_enabled( $settings ) && $provider->supports_response_format(); } diff --git a/includes/Admin/class-groq-ai-logs-table.php b/includes/Admin/class-groq-ai-logs-table.php index b3170e4..cccfc74 100644 --- a/includes/Admin/class-groq-ai-logs-table.php +++ b/includes/Admin/class-groq-ai-logs-table.php @@ -30,13 +30,13 @@ class Groq_AI_Logs_Table extends WP_List_Table { public function get_columns() { return [ - 'created_at' => __( 'Datum', 'groq-ai-product-text' ), - 'user_id' => __( 'Gebruiker', 'groq-ai-product-text' ), - 'post_title' => __( 'Product', 'groq-ai-product-text' ), - 'provider' => __( 'Provider', 'groq-ai-product-text' ), - 'model' => __( 'Model', 'groq-ai-product-text' ), - 'status' => __( 'Status', 'groq-ai-product-text' ), - 'tokens_total' => __( 'Tokens', 'groq-ai-product-text' ), + 'created_at' => __( 'Datum', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'user_id' => __( 'Gebruiker', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'post_title' => __( 'Product', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'provider' => __( 'Provider', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'model' => __( 'Model', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'status' => __( 'Status', GROQ_AI_PRODUCT_TEXT_DOMAIN ), + 'tokens_total' => __( 'Tokens', GROQ_AI_PRODUCT_TEXT_DOMAIN ), ]; } @@ -114,7 +114,7 @@ class Groq_AI_Logs_Table extends WP_List_Table { if ( ! $item['post_id'] ) { return '—'; } - $title = $item['post_title'] ? $item['post_title'] : sprintf( __( 'Product #%d', 'groq-ai-product-text' ), (int) $item['post_id'] ); + $title = $item['post_title'] ? $item['post_title'] : sprintf( __( 'Product #%d', GROQ_AI_PRODUCT_TEXT_DOMAIN ), (int) $item['post_id'] ); $link = get_edit_post_link( $item['post_id'] ); return $link ? sprintf( '%s', esc_url( $link ), esc_html( $title ) ) : esc_html( $title ); case 'user_id': @@ -131,7 +131,7 @@ class Groq_AI_Logs_Table extends WP_List_Table { } public function no_items() { - esc_html_e( 'Nog geen AI-logboeken gevonden.', 'groq-ai-product-text' ); + esc_html_e( 'Nog geen AI-logboeken gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); } protected function column_created_at( $item ) { diff --git a/includes/Admin/class-groq-ai-product-ui.php b/includes/Admin/class-groq-ai-product-ui.php index f436517..0b3ffd2 100644 --- a/includes/Admin/class-groq-ai-product-ui.php +++ b/includes/Admin/class-groq-ai-product-ui.php @@ -14,7 +14,7 @@ class Groq_AI_Product_Text_Product_UI { public function register_meta_box() { add_meta_box( 'groq-ai-generator-box', - __( 'Gebruik AI', 'groq-ai-product-text' ), + __( 'Gebruik AI', GROQ_AI_PRODUCT_TEXT_DOMAIN ), [ $this, 'render_meta_box' ], 'product', 'side', @@ -24,14 +24,14 @@ class Groq_AI_Product_Text_Product_UI { public function render_meta_box() { if ( ! current_user_can( 'edit_products' ) ) { - echo '

' . esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', 'groq-ai-product-text' ) . '

'; + echo '

' . esc_html__( 'Je hebt geen toestemming om deze actie uit te voeren.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) . '

'; return; } ?> -

- +

+

- +