Add admin classes for AI logs and settings management
- Introduced Groq_AI_Logs_Admin class to manage AI logs in the admin panel, including log viewing and detail rendering. - Created Groq_AI_Settings_Renderer class for rendering settings fields in a structured manner. - Implemented Groq_AI_Term_Admin_Base class to handle term-related functionalities, including term page registration and bulk actions for term descriptions. - Enhanced term management with AJAX support for generating term descriptions and handling Rank Math integration.
This commit is contained in:
@@ -58,9 +58,15 @@ require_once __DIR__ . '/includes/Services/Google/class-groq-ai-google-oauth-cli
|
|||||||
require_once __DIR__ . '/includes/Services/Google/class-groq-ai-google-search-console-client.php';
|
require_once __DIR__ . '/includes/Services/Google/class-groq-ai-google-search-console-client.php';
|
||||||
require_once __DIR__ . '/includes/Services/Google/class-groq-ai-google-analytics-data-client.php';
|
require_once __DIR__ . '/includes/Services/Google/class-groq-ai-google-analytics-data-client.php';
|
||||||
require_once __DIR__ . '/includes/Services/Google/class-groq-ai-google-context-builder.php';
|
require_once __DIR__ . '/includes/Services/Google/class-groq-ai-google-context-builder.php';
|
||||||
|
require_once __DIR__ . '/includes/Admin/class-groq-ai-admin-base.php';
|
||||||
|
require_once __DIR__ . '/includes/Admin/class-groq-ai-term-admin-base.php';
|
||||||
|
require_once __DIR__ . '/includes/Admin/class-groq-ai-categories-admin.php';
|
||||||
|
require_once __DIR__ . '/includes/Admin/class-groq-ai-brands-admin.php';
|
||||||
require_once __DIR__ . '/includes/Admin/class-groq-ai-settings-page.php';
|
require_once __DIR__ . '/includes/Admin/class-groq-ai-settings-page.php';
|
||||||
|
require_once __DIR__ . '/includes/Admin/class-groq-ai-logs-admin.php';
|
||||||
require_once __DIR__ . '/includes/Admin/class-groq-ai-logs-table.php';
|
require_once __DIR__ . '/includes/Admin/class-groq-ai-logs-table.php';
|
||||||
require_once __DIR__ . '/includes/Admin/class-groq-ai-product-ui.php';
|
require_once __DIR__ . '/includes/Admin/class-groq-ai-product-ui.php';
|
||||||
|
require_once __DIR__ . '/includes/Admin/class-groq-ai-settings-renderer.php';
|
||||||
|
|
||||||
if( ! class_exists( 'SitiWebUpdater' ) ){
|
if( ! class_exists( 'SitiWebUpdater' ) ){
|
||||||
include_once( plugin_dir_path( __FILE__ ) . 'SitiWebUpdater.php' );
|
include_once( plugin_dir_path( __FILE__ ) . 'SitiWebUpdater.php' );
|
||||||
@@ -85,9 +91,18 @@ final class Groq_AI_Product_Text_Plugin {
|
|||||||
/** @var Groq_AI_Service_Container */
|
/** @var Groq_AI_Service_Container */
|
||||||
private $container;
|
private $container;
|
||||||
|
|
||||||
/** @var */
|
/** @var Groq_AI_Product_Text_Settings_Page */
|
||||||
private $settings_page;
|
private $settings_page;
|
||||||
|
|
||||||
|
/** @var Groq_AI_Categories_Admin */
|
||||||
|
private $categories_admin;
|
||||||
|
|
||||||
|
/** @var Groq_AI_Brands_Admin */
|
||||||
|
private $brands_admin;
|
||||||
|
|
||||||
|
/** @var Groq_AI_Logs_Admin */
|
||||||
|
private $logs_admin;
|
||||||
|
|
||||||
/** @var Groq_AI_Product_Text_Product_UI */
|
/** @var Groq_AI_Product_Text_Product_UI */
|
||||||
private $product_ui;
|
private $product_ui;
|
||||||
|
|
||||||
@@ -106,6 +121,9 @@ final class Groq_AI_Product_Text_Plugin {
|
|||||||
$this->register_services();
|
$this->register_services();
|
||||||
|
|
||||||
$this->settings_page = new Groq_AI_Product_Text_Settings_Page( $this, $this->get_provider_manager() );
|
$this->settings_page = new Groq_AI_Product_Text_Settings_Page( $this, $this->get_provider_manager() );
|
||||||
|
$this->categories_admin = new Groq_AI_Categories_Admin( $this );
|
||||||
|
$this->brands_admin = new Groq_AI_Brands_Admin( $this );
|
||||||
|
$this->logs_admin = new Groq_AI_Logs_Admin( $this );
|
||||||
$this->product_ui = new Groq_AI_Product_Text_Product_UI( $this );
|
$this->product_ui = new Groq_AI_Product_Text_Product_UI( $this );
|
||||||
|
|
||||||
add_action( 'init', [ $this, 'load_textdomain' ] );
|
add_action( 'init', [ $this, 'load_textdomain' ] );
|
||||||
@@ -366,6 +384,14 @@ final class Groq_AI_Product_Text_Plugin {
|
|||||||
return $this->get_settings_manager()->get_loggable_settings_snapshot( $settings );
|
return $this->get_settings_manager()->get_loggable_settings_snapshot( $settings );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function create_settings_renderer( $values = null ) {
|
||||||
|
if ( null === $values ) {
|
||||||
|
$values = $this->get_settings();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Groq_AI_Settings_Renderer( self::OPTION_KEY, $values );
|
||||||
|
}
|
||||||
|
|
||||||
public function should_use_response_format( Groq_AI_Provider_Interface $provider, $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();
|
return ! $this->is_response_format_compat_enabled( $settings ) && $provider->supports_response_format();
|
||||||
}
|
}
|
||||||
|
|||||||
46
includes/Admin/class-groq-ai-admin-base.php
Normal file
46
includes/Admin/class-groq-ai-admin-base.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class Groq_AI_Admin_Base {
|
||||||
|
/** @var Groq_AI_Product_Text_Plugin */
|
||||||
|
protected $plugin;
|
||||||
|
|
||||||
|
public function __construct( Groq_AI_Product_Text_Plugin $plugin ) {
|
||||||
|
$this->plugin = $plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function current_user_can_manage() {
|
||||||
|
return current_user_can( 'manage_options' );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function enqueue_admin_styles() {
|
||||||
|
wp_enqueue_style(
|
||||||
|
'groq-ai-settings',
|
||||||
|
plugins_url( 'assets/css/admin.css', GROQ_AI_PRODUCT_TEXT_FILE ),
|
||||||
|
[],
|
||||||
|
GROQ_AI_PRODUCT_TEXT_VERSION
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_enqueue_style(
|
||||||
|
'groq-ai-settings-extra',
|
||||||
|
plugins_url( 'assets/css/settings.css', GROQ_AI_PRODUCT_TEXT_FILE ),
|
||||||
|
[ 'groq-ai-settings' ],
|
||||||
|
GROQ_AI_PRODUCT_TEXT_VERSION
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
177
includes/Admin/class-groq-ai-brands-admin.php
Normal file
177
includes/Admin/class-groq-ai-brands-admin.php
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Groq_AI_Brands_Admin extends Groq_AI_Term_Admin_Base {
|
||||||
|
private $brand_taxonomy = null;
|
||||||
|
|
||||||
|
public function __construct( Groq_AI_Product_Text_Plugin $plugin ) {
|
||||||
|
parent::__construct( $plugin );
|
||||||
|
add_action( 'admin_menu', [ $this, 'register_menu_pages' ] );
|
||||||
|
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_brand_assets' ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register_menu_pages() {
|
||||||
|
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' ]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->register_term_page();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_brands_overview_page() {
|
||||||
|
if ( ! $this->current_user_can_manage() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$taxonomy = $this->detect_brand_taxonomy();
|
||||||
|
if ( '' === $taxonomy ) {
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php esc_html_e( 'Merk teksten', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||||
|
<p><?php esc_html_e( 'Geen merk-taxonomie gevonden. Installeer/activeer een merken-plugin of stel een taxonomie in via de filter groq_ai_brand_taxonomy.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$overview = $this->get_term_overview_data( $taxonomy );
|
||||||
|
$rows = isset( $overview['rows'] ) ? $overview['rows'] : [];
|
||||||
|
$empty_count = isset( $overview['empty_count'] ) ? (int) $overview['empty_count'] : 0;
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php esc_html_e( 'Merk teksten', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
printf(
|
||||||
|
/* translators: %s: taxonomy key */
|
||||||
|
esc_html__( 'Gedetecteerde merk-taxonomie: %s', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
|
||||||
|
esc_html( $taxonomy )
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
<?php $this->render_term_bulk_panel( __( 'merken', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $empty_count ); ?>
|
||||||
|
<p class="description"><?php esc_html_e( 'Gebruik de knop "Genereer opnieuw" in de tabel om bestaande merkteksten opnieuw laten schrijven.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
<table class="widefat striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php esc_html_e( 'Merk', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Slug', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Producten', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Woorden (omschrijving)', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Acties', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ( empty( $rows ) ) : ?>
|
||||||
|
<tr><td colspan="5"><?php esc_html_e( 'Geen merken gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></td></tr>
|
||||||
|
<?php else : ?>
|
||||||
|
<?php foreach ( $rows as $row ) : ?>
|
||||||
|
<?php
|
||||||
|
$row_classes = [ 'groq-ai-term-row' ];
|
||||||
|
if ( empty( $row['has_description'] ) ) {
|
||||||
|
$row_classes[] = 'groq-ai-term-missing';
|
||||||
|
}
|
||||||
|
$link = isset( $row['url'] ) ? $row['url'] : '';
|
||||||
|
$count = isset( $row['count'] ) ? (int) $row['count'] : 0;
|
||||||
|
$words = isset( $row['words'] ) ? (int) $row['words'] : 0;
|
||||||
|
?>
|
||||||
|
<tr class="<?php echo esc_attr( implode( ' ', $row_classes ) ); ?>" data-groq-ai-term-id="<?php echo esc_attr( isset( $row['id'] ) ? (string) $row['id'] : '' ); ?>">
|
||||||
|
<td>
|
||||||
|
<a href="<?php echo esc_url( $link ); ?>"><strong><?php echo esc_html( isset( $row['name'] ) ? $row['name'] : '' ); ?></strong></a>
|
||||||
|
</td>
|
||||||
|
<td><?php echo esc_html( isset( $row['slug'] ) ? $row['slug'] : '' ); ?></td>
|
||||||
|
<td><?php echo esc_html( (string) $count ); ?></td>
|
||||||
|
<td class="groq-ai-word-cell"><span class="groq-ai-word-count"><?php echo esc_html( (string) $words ); ?></span></td>
|
||||||
|
<td class="groq-ai-term-actions">
|
||||||
|
<button type="button" class="button button-secondary groq-ai-regenerate-term" data-term-id="<?php echo esc_attr( isset( $row['id'] ) ? (string) $row['id'] : '' ); ?>">
|
||||||
|
<?php esc_html_e( 'Genereer opnieuw', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enqueue_brand_assets( $hook ) {
|
||||||
|
if ( 0 !== strpos( (string) $hook, 'settings_page_groq-ai-product-text-brands' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->enqueue_admin_styles();
|
||||||
|
|
||||||
|
$taxonomy = $this->detect_brand_taxonomy();
|
||||||
|
if ( '' === $taxonomy ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
$taxonomy,
|
||||||
|
[
|
||||||
|
'allowRegenerate' => true,
|
||||||
|
'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 ),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function detect_brand_taxonomy() {
|
||||||
|
if ( null !== $this->brand_taxonomy ) {
|
||||||
|
return $this->brand_taxonomy;
|
||||||
|
}
|
||||||
|
|
||||||
|
$candidates = [
|
||||||
|
'product_brand',
|
||||||
|
'pwb-brand',
|
||||||
|
'yith_product_brand',
|
||||||
|
'berocket_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
119
includes/Admin/class-groq-ai-categories-admin.php
Normal file
119
includes/Admin/class-groq-ai-categories-admin.php
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Groq_AI_Categories_Admin extends Groq_AI_Term_Admin_Base {
|
||||||
|
public function __construct( Groq_AI_Product_Text_Plugin $plugin ) {
|
||||||
|
parent::__construct( $plugin );
|
||||||
|
add_action( 'admin_menu', [ $this, 'register_menu_pages' ] );
|
||||||
|
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_category_assets' ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register_menu_pages() {
|
||||||
|
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' ]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->register_term_page();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_categories_overview_page() {
|
||||||
|
if ( ! $this->current_user_can_manage() ) {
|
||||||
|
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;
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php esc_html_e( 'Categorie teksten', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||||
|
<p><?php esc_html_e( 'Klik op een categorie om teksten te genereren en instellingen te beheren. De tabel toont de huidige woordlengte van de categorie-omschrijving.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
<?php $this->render_term_bulk_panel( __( 'categorieën', GROQ_AI_PRODUCT_TEXT_DOMAIN ), $empty_count ); ?>
|
||||||
|
<table class="widefat striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php esc_html_e( 'Categorie', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Slug', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Producten', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Woorden (omschrijving)', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Acties', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ( empty( $rows ) ) : ?>
|
||||||
|
<tr><td colspan="5"><?php esc_html_e( 'Geen categorieën gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></td></tr>
|
||||||
|
<?php else : ?>
|
||||||
|
<?php foreach ( $rows as $row ) : ?>
|
||||||
|
<?php
|
||||||
|
$row_classes = [ 'groq-ai-term-row' ];
|
||||||
|
if ( empty( $row['has_description'] ) ) {
|
||||||
|
$row_classes[] = 'groq-ai-term-missing';
|
||||||
|
}
|
||||||
|
$link = isset( $row['url'] ) ? $row['url'] : '';
|
||||||
|
$count = isset( $row['count'] ) ? (int) $row['count'] : 0;
|
||||||
|
$words = isset( $row['words'] ) ? (int) $row['words'] : 0;
|
||||||
|
?>
|
||||||
|
<tr class="<?php echo esc_attr( implode( ' ', $row_classes ) ); ?>" data-groq-ai-term-id="<?php echo esc_attr( isset( $row['id'] ) ? (string) $row['id'] : '' ); ?>">
|
||||||
|
<td>
|
||||||
|
<a href="<?php echo esc_url( $link ); ?>"><strong><?php echo esc_html( isset( $row['name'] ) ? $row['name'] : '' ); ?></strong></a>
|
||||||
|
</td>
|
||||||
|
<td><?php echo esc_html( isset( $row['slug'] ) ? $row['slug'] : '' ); ?></td>
|
||||||
|
<td><?php echo esc_html( (string) $count ); ?></td>
|
||||||
|
<td class="groq-ai-word-cell"><span class="groq-ai-word-count"><?php echo esc_html( (string) $words ); ?></span></td>
|
||||||
|
<td class="groq-ai-term-actions">
|
||||||
|
<button type="button" class="button button-secondary groq-ai-regenerate-term" data-term-id="<?php echo esc_attr( isset( $row['id'] ) ? (string) $row['id'] : '' ); ?>">
|
||||||
|
<?php esc_html_e( 'Genereer opnieuw', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enqueue_category_assets( $hook ) {
|
||||||
|
if ( 0 !== strpos( (string) $hook, 'settings_page_groq-ai-product-text-categories' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->enqueue_admin_styles();
|
||||||
|
|
||||||
|
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(
|
||||||
|
'product_cat',
|
||||||
|
[
|
||||||
|
'allowRegenerate' => true,
|
||||||
|
'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 ),
|
||||||
|
'confirmRegenerate' => __( 'Wil je categorie %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 categorie opnieuw genereert.', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
180
includes/Admin/class-groq-ai-logs-admin.php
Normal file
180
includes/Admin/class-groq-ai-logs-admin.php
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Groq_AI_Logs_Admin extends Groq_AI_Admin_Base {
|
||||||
|
public function __construct( Groq_AI_Product_Text_Plugin $plugin ) {
|
||||||
|
parent::__construct( $plugin );
|
||||||
|
add_action( 'admin_menu', [ $this, 'register_menu_pages' ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register_menu_pages() {
|
||||||
|
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 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' ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_logs_page() {
|
||||||
|
if ( ! $this->current_user_can_manage() ) {
|
||||||
|
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 ( ! $this->current_user_can_manage() ) {
|
||||||
|
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 if ( ! empty( $log['request_json'] ) ) :
|
||||||
|
$request_params = json_decode( $log['request_json'], true );
|
||||||
|
$request_params = is_array( $request_params ) ? $request_params : [];
|
||||||
|
if ( ! empty( $request_params ) ) :
|
||||||
|
$request_pretty = wp_json_encode( $request_params, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
|
||||||
|
$request_pretty = $request_pretty ? $request_pretty : wp_json_encode( $request_params );
|
||||||
|
?>
|
||||||
|
<h2><?php esc_html_e( 'Request parameters', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h2>
|
||||||
|
<pre style="background:#fff;border:1px solid #dcdcde;padding:12px;white-space:pre-wrap;">
|
||||||
|
<?php echo esc_html( $request_pretty ); ?>
|
||||||
|
</pre>
|
||||||
|
<?php endif; endif; ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty( $log['usage_json'] ) ) :
|
||||||
|
$usage_meta = json_decode( $log['usage_json'], true );
|
||||||
|
$usage_meta = is_array( $usage_meta ) ? $usage_meta : [];
|
||||||
|
if ( ! empty( $usage_meta ) ) :
|
||||||
|
$usage_pretty = wp_json_encode( $usage_meta, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
|
||||||
|
$usage_pretty = $usage_pretty ? $usage_pretty : wp_json_encode( $usage_meta );
|
||||||
|
?>
|
||||||
|
<h2><?php esc_html_e( 'Usage metadata', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h2>
|
||||||
|
<pre style="background:#f6f7f7;border:1px solid #dcdcde;padding:12px;white-space:pre-wrap;">
|
||||||
|
<?php echo esc_html( $usage_pretty ); ?>
|
||||||
|
</pre>
|
||||||
|
<?php endif; endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
262
includes/Admin/class-groq-ai-settings-renderer.php
Normal file
262
includes/Admin/class-groq-ai-settings-renderer.php
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Groq_AI_Settings_Renderer {
|
||||||
|
/** @var string */
|
||||||
|
private $option_key;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $values = [];
|
||||||
|
|
||||||
|
public function __construct( $option_key, $values = [] ) {
|
||||||
|
$this->option_key = $option_key;
|
||||||
|
$this->set_values( $values );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set_values( $values ) {
|
||||||
|
$this->values = is_array( $values ) ? $values : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function open_table( $args = [] ) {
|
||||||
|
$defaults = [
|
||||||
|
'class' => 'form-table',
|
||||||
|
'role' => 'presentation',
|
||||||
|
];
|
||||||
|
$args = wp_parse_args( $args, $defaults );
|
||||||
|
|
||||||
|
printf( '<table %s>', $this->build_attr_string( $args ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close_table() {
|
||||||
|
echo '</table>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function field( $args ) {
|
||||||
|
$defaults = [
|
||||||
|
'key' => '',
|
||||||
|
'name' => '',
|
||||||
|
'id' => '',
|
||||||
|
'label' => '',
|
||||||
|
'description' => '',
|
||||||
|
'type' => 'text',
|
||||||
|
'placeholder' => '',
|
||||||
|
'options' => [],
|
||||||
|
'attributes' => [],
|
||||||
|
'default' => '',
|
||||||
|
'value' => null,
|
||||||
|
'renderer' => null,
|
||||||
|
'row_attributes' => [],
|
||||||
|
'row_class' => '',
|
||||||
|
];
|
||||||
|
$args = wp_parse_args( $args, $defaults );
|
||||||
|
|
||||||
|
if ( '' === $args['name'] && '' !== $args['key'] ) {
|
||||||
|
$args['name'] = $this->build_field_name( $args['key'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( '' === $args['id'] && '' !== $args['key'] ) {
|
||||||
|
$args['id'] = $this->build_field_id( $args['key'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( null === $args['value'] && '' !== $args['key'] ) {
|
||||||
|
$args['value'] = $this->get_value( $args['key'], $args['default'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! isset( $args['attributes']['id'] ) && '' !== $args['id'] ) {
|
||||||
|
$args['attributes']['id'] = $args['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $args['type'];
|
||||||
|
|
||||||
|
$row_attributes = $this->prepare_row_attributes( $args );
|
||||||
|
$row_attr_string = $row_attributes ? ' ' . $this->build_attr_string( $row_attributes ) : '';
|
||||||
|
|
||||||
|
echo '<tr' . $row_attr_string . '>';
|
||||||
|
$this->render_label_cell( $args );
|
||||||
|
echo '<td>';
|
||||||
|
|
||||||
|
if ( is_callable( $args['renderer'] ) ) {
|
||||||
|
call_user_func( $args['renderer'], $args, $this );
|
||||||
|
} else {
|
||||||
|
switch ( $type ) {
|
||||||
|
case 'textarea':
|
||||||
|
$this->render_textarea( $args );
|
||||||
|
break;
|
||||||
|
case 'password':
|
||||||
|
$this->render_input( 'password', $args );
|
||||||
|
break;
|
||||||
|
case 'number':
|
||||||
|
$this->render_input( 'number', $args );
|
||||||
|
break;
|
||||||
|
case 'select':
|
||||||
|
$this->render_select( $args );
|
||||||
|
break;
|
||||||
|
case 'checkbox':
|
||||||
|
$this->render_checkbox( $args );
|
||||||
|
break;
|
||||||
|
case 'toggle':
|
||||||
|
$this->render_toggle( $args );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->render_input( 'text', $args );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->render_description( $args['description'] );
|
||||||
|
|
||||||
|
echo '</td>';
|
||||||
|
echo '</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render_label_cell( $args ) {
|
||||||
|
$label = $args['label'];
|
||||||
|
$id = $args['id'];
|
||||||
|
echo '<th scope="row">';
|
||||||
|
if ( '' !== $label ) {
|
||||||
|
printf( '<label for="%s">%s</label>', esc_attr( $id ), esc_html( $label ) );
|
||||||
|
}
|
||||||
|
echo '</th>';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render_input( $type, $args ) {
|
||||||
|
$attributes = $this->prepare_input_attributes( $args );
|
||||||
|
printf( '<input type="%s" %s />', esc_attr( $type ), $attributes );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render_textarea( $args ) {
|
||||||
|
$attributes = $this->prepare_input_attributes( $args, [ 'rows' => 4, 'class' => 'large-text' ] );
|
||||||
|
printf( '<textarea %s>%s</textarea>', $attributes, esc_textarea( $args['value'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render_select( $args ) {
|
||||||
|
$attributes = $this->prepare_input_attributes( $args );
|
||||||
|
printf( '<select %s>', $attributes );
|
||||||
|
foreach ( (array) $args['options'] as $value => $label ) {
|
||||||
|
printf( '<option value="%s" %s>%s</option>', esc_attr( $value ), selected( $args['value'], $value, false ), esc_html( $label ) );
|
||||||
|
}
|
||||||
|
echo '</select>';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render_checkbox( $args ) {
|
||||||
|
$value = ! empty( $args['value'] );
|
||||||
|
$attributes = $this->prepare_input_attributes( $args, [ 'class' => '' ] );
|
||||||
|
printf( '<label><input type="checkbox" %s %s /> %s</label>', $attributes, checked( $value, true, false ), esc_html( $args['checkbox_label'] ?? '' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render_toggle( $args ) {
|
||||||
|
$value = ! empty( $args['value'] );
|
||||||
|
$attributes = $this->prepare_input_attributes( $args, [ 'class' => '' ] );
|
||||||
|
printf( '<label class="groq-ai-toggle"><input type="checkbox" %s %s /> <span class="groq-ai-toggle__slider"></span> %s</label>', $attributes, checked( $value, true, false ), esc_html( $args['checkbox_label'] ?? '' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render_description( $text ) {
|
||||||
|
$text = trim( (string) $text );
|
||||||
|
if ( '' === $text ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf( '<p class="description">%s</p>', wp_kses_post( $text ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function prepare_input_attributes( $args, $defaults = [] ) {
|
||||||
|
$attributes = wp_parse_args( $args['attributes'], $defaults );
|
||||||
|
$attributes['name'] = $args['name'];
|
||||||
|
|
||||||
|
if ( ! isset( $attributes['id'] ) ) {
|
||||||
|
$attributes['id'] = $args['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( '' !== $args['placeholder'] ) {
|
||||||
|
$attributes['placeholder'] = $args['placeholder'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! isset( $attributes['class'] ) ) {
|
||||||
|
$attributes['class'] = 'regular-text';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! in_array( $args['type'], [ 'checkbox', 'toggle', 'select', 'textarea' ], true ) ) {
|
||||||
|
$attributes['value'] = $args['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->build_attr_string( $attributes );
|
||||||
|
}
|
||||||
|
private function prepare_row_attributes( $args ) {
|
||||||
|
$attributes = [];
|
||||||
|
if ( isset( $args['row_attributes'] ) && is_array( $args['row_attributes'] ) ) {
|
||||||
|
$attributes = $args['row_attributes'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$row_class = isset( $args['row_class'] ) ? trim( (string) $args['row_class'] ) : '';
|
||||||
|
if ( '' !== $row_class ) {
|
||||||
|
if ( isset( $attributes['class'] ) ) {
|
||||||
|
$attributes['class'] .= ' ' . $row_class;
|
||||||
|
} else {
|
||||||
|
$attributes['class'] = $row_class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_filter(
|
||||||
|
$attributes,
|
||||||
|
function ( $value ) {
|
||||||
|
return '' !== $value || 0 === $value || '0' === $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function build_attr_string( $attributes ) {
|
||||||
|
$buffer = [];
|
||||||
|
foreach ( $attributes as $key => $value ) {
|
||||||
|
if ( '' === $value && 0 !== $value && '0' !== $value ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$buffer[] = sprintf( '%s="%s"', esc_attr( $key ), esc_attr( $value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode( ' ', $buffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function build_field_name( $key ) {
|
||||||
|
$segments = $this->split_key( $key );
|
||||||
|
$name = $this->option_key;
|
||||||
|
|
||||||
|
foreach ( $segments as $segment ) {
|
||||||
|
$name .= '[' . $segment . ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function build_field_id( $key ) {
|
||||||
|
$segments = $this->split_key( $key );
|
||||||
|
|
||||||
|
return 'groq-ai-' . implode( '-', $segments );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_value( $key, $default = '' ) {
|
||||||
|
$segments = $this->split_key( $key );
|
||||||
|
$value = $this->values;
|
||||||
|
|
||||||
|
foreach ( $segments as $segment ) {
|
||||||
|
if ( is_array( $value ) && array_key_exists( $segment, $value ) ) {
|
||||||
|
$value = $value[ $segment ];
|
||||||
|
} else {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function split_key( $key ) {
|
||||||
|
if ( is_array( $key ) ) {
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = trim( (string) $key );
|
||||||
|
if ( '' === $key ) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_map( 'sanitize_key', explode( '.', $key ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
520
includes/Admin/class-groq-ai-term-admin-base.php
Normal file
520
includes/Admin/class-groq-ai-term-admin-base.php
Normal file
@@ -0,0 +1,520 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class Groq_AI_Term_Admin_Base extends Groq_AI_Admin_Base {
|
||||||
|
protected $term_overview_cache = [];
|
||||||
|
|
||||||
|
private static $term_page_registered = false;
|
||||||
|
private static $term_handler_registered = false;
|
||||||
|
private static $term_assets_hook_registered = false;
|
||||||
|
|
||||||
|
public function __construct( Groq_AI_Product_Text_Plugin $plugin ) {
|
||||||
|
parent::__construct( $plugin );
|
||||||
|
$this->ensure_term_handler_registered();
|
||||||
|
$this->ensure_term_assets_hook();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function register_term_page() {
|
||||||
|
if ( self::$term_page_registered ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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' ]
|
||||||
|
);
|
||||||
|
|
||||||
|
self::$term_page_registered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function render_term_bulk_panel( $label_plural, $empty_count ) {
|
||||||
|
$label_plural = (string) $label_plural;
|
||||||
|
?>
|
||||||
|
<div class="groq-ai-bulk-panel">
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
if ( $empty_count > 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 )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
<p class="groq-ai-bulk-actions">
|
||||||
|
<?php
|
||||||
|
$button_label = sprintf(
|
||||||
|
esc_html__( 'Genereer teksten voor lege %s', GROQ_AI_PRODUCT_TEXT_DOMAIN ),
|
||||||
|
$label_plural
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<button type="button" class="button button-primary" id="groq-ai-bulk-generate"><?php echo esc_html( $button_label ); ?></button>
|
||||||
|
<button type="button" class="button" id="groq-ai-bulk-cancel" hidden><?php esc_html_e( 'Stop bulk generatie', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></button>
|
||||||
|
</p>
|
||||||
|
<div id="groq-ai-bulk-status" class="description"></div>
|
||||||
|
<ol id="groq-ai-bulk-log" class="groq-ai-bulk-log"></ol>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function localize_term_bulk_script( $taxonomy, $overrides = [] ) {
|
||||||
|
$overview = $this->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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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 count_words( $text ) {
|
||||||
|
$text = wp_strip_all_tags( (string) $text );
|
||||||
|
$text = trim( preg_replace( '/\s+/u', ' ', $text ) );
|
||||||
|
if ( '' === $text ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ( preg_match_all( '/\pL[\pL\pN\']*/u', $text, $matches ) ) {
|
||||||
|
return count( $matches[0] );
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enqueue_term_assets( $hook ) {
|
||||||
|
if ( 0 !== strpos( (string) $hook, 'settings_page_groq-ai-product-text-term' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->enqueue_admin_styles();
|
||||||
|
|
||||||
|
wp_enqueue_script(
|
||||||
|
'groq-ai-term-admin',
|
||||||
|
plugins_url( 'assets/js/term-admin.js', GROQ_AI_PRODUCT_TEXT_FILE ),
|
||||||
|
[],
|
||||||
|
GROQ_AI_PRODUCT_TEXT_VERSION,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
$taxonomy = isset( $_GET['taxonomy'] ) ? sanitize_key( wp_unslash( $_GET['taxonomy'] ) ) : '';
|
||||||
|
$term_id = isset( $_GET['term_id'] ) ? absint( $_GET['term_id'] ) : 0;
|
||||||
|
|
||||||
|
wp_localize_script(
|
||||||
|
'groq-ai-term-admin',
|
||||||
|
'GroqAITermGenerator',
|
||||||
|
[
|
||||||
|
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||||
|
'nonce' => wp_create_nonce( 'groq_ai_generate_term' ),
|
||||||
|
'taxonomy' => $taxonomy,
|
||||||
|
'termId' => $term_id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_term_generator_page() {
|
||||||
|
if ( ! $this->current_user_can_manage() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$taxonomy = isset( $_GET['taxonomy'] ) ? sanitize_key( wp_unslash( $_GET['taxonomy'] ) ) : '';
|
||||||
|
$term_id = isset( $_GET['term_id'] ) ? absint( $_GET['term_id'] ) : 0;
|
||||||
|
|
||||||
|
if ( '' === $taxonomy || ! taxonomy_exists( $taxonomy ) || ! $term_id ) {
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php esc_html_e( 'Term tekst', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||||
|
<p><?php esc_html_e( 'Ongeldige term.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$term = get_term( $term_id, $taxonomy );
|
||||||
|
if ( ! $term || is_wp_error( $term ) ) {
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php esc_html_e( 'Term tekst', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h1>
|
||||||
|
<p><?php esc_html_e( 'Term niet gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
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 );
|
||||||
|
$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 );
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<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; ?>
|
||||||
|
<table class="form-table" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<th><?php esc_html_e( 'Taxonomie', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<td><?php echo esc_html( $taxonomy ); ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><?php esc_html_e( 'Huidige woordtelling', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></th>
|
||||||
|
<td><?php echo esc_html( (string) $word_count ); ?></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" id="groq-ai-term-form">
|
||||||
|
<?php wp_nonce_field( 'groq_ai_save_term_content' ); ?>
|
||||||
|
<input type="hidden" name="action" value="groq_ai_save_term_content" />
|
||||||
|
<input type="hidden" name="taxonomy" value="<?php echo esc_attr( $taxonomy ); ?>" />
|
||||||
|
<input type="hidden" name="term_id" value="<?php echo esc_attr( $term_id ); ?>" />
|
||||||
|
<table class="form-table" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="groq-ai-term-description"><?php esc_html_e( 'Omschrijving (top description)', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<textarea id="groq-ai-term-description" class="large-text" rows="8" name="description"><?php echo esc_textarea( $term->description ); ?></textarea>
|
||||||
|
<p class="description"><?php esc_html_e( 'Bovenste omschrijving van de term. Wordt op de term-archive bovenaan getoond.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="groq-ai-term-bottom"><?php esc_html_e( 'Onderste omschrijving', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<textarea id="groq-ai-term-bottom" class="large-text" rows="10" name="groq_ai_term_bottom_description"><?php echo esc_textarea( $bottom_description ); ?></textarea>
|
||||||
|
<p class="description"><?php esc_html_e( 'Wordt onderaan op de term-archive geplaatst. Laat leeg wanneer je dit niet wilt gebruiken.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="groq-ai-term-custom-prompt"><?php esc_html_e( 'Eigen prompt (optioneel)', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<textarea id="groq-ai-term-custom-prompt" class="large-text" rows="5" name="groq_ai_term_custom_prompt"><?php echo esc_textarea( $meta_prompt ); ?></textarea>
|
||||||
|
<p class="description"><?php esc_html_e( 'Overschrijft de standaard prompt alleen voor deze term.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php if ( $rankmath_module_enabled ) : ?>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="groq-ai-rankmath-title"><?php esc_html_e( 'Rank Math meta title', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<textarea id="groq-ai-rankmath-title" class="large-text" rows="2" name="groq_ai_rankmath_meta_title" <?php disabled( ! $rankmath_active ); ?>><?php echo esc_textarea( $rankmath_title ); ?></textarea>
|
||||||
|
<p class="description"><?php esc_html_e( 'Wordt opgeslagen in Rank Math. Alleen beschikbaar als Rank Math actief is.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="groq-ai-rankmath-description"><?php esc_html_e( 'Rank Math meta description', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<textarea id="groq-ai-rankmath-description" class="large-text" rows="3" name="groq_ai_rankmath_meta_description" <?php disabled( ! $rankmath_active ); ?>><?php echo esc_textarea( $rankmath_description ); ?></textarea>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="groq-ai-rankmath-keywords"><?php esc_html_e( 'Rank Math focus keywords', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<textarea id="groq-ai-rankmath-keywords" class="large-text" rows="2" name="groq_ai_rankmath_focus_keywords" <?php disabled( ! $rankmath_active ); ?>><?php echo esc_textarea( $rankmath_focus_keywords ); ?></textarea>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</table>
|
||||||
|
<?php submit_button( __( 'Term opslaan', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ); ?>
|
||||||
|
</form>
|
||||||
|
<hr />
|
||||||
|
<form id="groq-ai-term-generator" action="javascript:void(0);">
|
||||||
|
<h2><?php esc_html_e( 'AI-term generator', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h2>
|
||||||
|
<p><?php esc_html_e( 'Gebruik de AI om automatisch teksten te genereren. Pas deze aan voordat je opslaat.', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></p>
|
||||||
|
<textarea id="groq-ai-term-prompt" class="large-text" rows="5"><?php echo esc_textarea( $default_prompt ); ?></textarea>
|
||||||
|
<p>
|
||||||
|
<button type="submit" class="button button-primary"><?php esc_html_e( 'Genereer', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></button>
|
||||||
|
<button type="button" class="button" id="groq-ai-term-apply"><?php esc_html_e( 'Zet in velden', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></button>
|
||||||
|
</p>
|
||||||
|
<div id="groq-ai-term-status" class="description" aria-live="polite"></div>
|
||||||
|
<h3><?php esc_html_e( 'Gegenereerde tekst (omschrijving, 1 alinea)', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h3>
|
||||||
|
<textarea id="groq-ai-term-generated-top" class="large-text" rows="6"></textarea>
|
||||||
|
<h3><?php esc_html_e( 'Gegenereerde tekst (onderaan)', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h3>
|
||||||
|
<textarea id="groq-ai-term-generated-bottom" class="large-text" rows="10"></textarea>
|
||||||
|
<?php if ( $rankmath_module_enabled ) : ?>
|
||||||
|
<h3><?php esc_html_e( 'Gegenereerde Rank Math meta title', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h3>
|
||||||
|
<textarea id="groq-ai-term-generated-meta-title" class="large-text" rows="2"></textarea>
|
||||||
|
<h3><?php esc_html_e( 'Gegenereerde Rank Math meta description', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h3>
|
||||||
|
<textarea id="groq-ai-term-generated-meta-description" class="large-text" rows="3"></textarea>
|
||||||
|
<h3><?php esc_html_e( 'Gegenereerde Rank Math focus keywords', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h3>
|
||||||
|
<textarea id="groq-ai-term-generated-focus-keywords" class="large-text" rows="2"></textarea>
|
||||||
|
<?php endif; ?>
|
||||||
|
<h3><?php esc_html_e( 'Ruwe JSON-output', GROQ_AI_PRODUCT_TEXT_DOMAIN ); ?></h3>
|
||||||
|
<pre id="groq-ai-term-raw" style="background:#fff;border:1px solid #ddd;padding:12px;max-height:240px;overflow:auto;"></pre>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolve_term_bottom_description_meta_key( $term, $settings ) {
|
||||||
|
$default_key = '';
|
||||||
|
if ( is_array( $settings ) && isset( $settings['term_bottom_description_meta_key'] ) ) {
|
||||||
|
$default_key = sanitize_key( (string) $settings['term_bottom_description_meta_key'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = apply_filters( 'groq_ai_term_bottom_description_meta_key', $default_key, $term, $settings );
|
||||||
|
$key = sanitize_key( (string) $key );
|
||||||
|
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',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
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 <p>-tags. Voeg geen prijsinformatie toe.', GROQ_AI_PRODUCT_TEXT_DOMAIN );
|
||||||
|
|
||||||
|
return apply_filters( 'groq_ai_default_term_prompt', $default_prompt, $term );
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ensure_term_handler_registered() {
|
||||||
|
if ( self::$term_handler_registered ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action( 'admin_post_groq_ai_save_term_content', [ $this, 'handle_save_term_content' ] );
|
||||||
|
self::$term_handler_registered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ensure_term_assets_hook() {
|
||||||
|
if ( self::$term_assets_hook_registered ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_term_assets' ] );
|
||||||
|
self::$term_assets_hook_registered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user