tags, NOT in heading tags
$allowed_tags = array('p', 'div');
$forbidden_tags = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'a');
// Skip if parent is a forbidden tag
if (in_array($parent_tag, $forbidden_tags)) {
continue;
}
// Only proceed if parent is an allowed tag
if (!in_array($parent_tag, $allowed_tags)) {
continue;
}
// Check if keyword exists in this part (case-insensitive)
$pos = stripos($part, $keyword);
if ($pos !== false) {
// Get the actual matched text (preserving case)
$matched_keyword = substr($part, $pos, strlen($keyword));
// Create link with the actual matched keyword (not escaped)
$link = '
' . $matched_keyword . '';
// Replace only first occurrence
$parts[$index] = substr_replace($part, $link, $pos, strlen($keyword));
$replaced = true;
}
}
// Rejoin all parts
$content = implode('', $parts);
return $content;
}
/**
* Get main keyword from RankMath or Yoast SEO
*/
private function get_main_keyword($post_id, $product_name) {
$main_keyword = '';
// Check for RankMath
if (defined('RANK_MATH_VERSION') || class_exists('RankMath')) {
$rankmath_keyword = get_post_meta($post_id, 'rank_math_focus_keyword', true);
if (!empty($rankmath_keyword)) {
$main_keyword = $rankmath_keyword;
} else {
// Set product name as focus keyword if not exists
update_post_meta($post_id, 'rank_math_focus_keyword', $product_name);
$main_keyword = $product_name;
}
}
// Check for Yoast SEO
elseif (defined('WPSEO_VERSION') || class_exists('WPSEO_Options')) {
$yoast_keyword = get_post_meta($post_id, '_yoast_wpseo_focuskw', true);
if (!empty($yoast_keyword)) {
$main_keyword = $yoast_keyword;
} else {
// Set product name as focus keyword if not exists
update_post_meta($post_id, '_yoast_wpseo_focuskw', $product_name);
$main_keyword = $product_name;
}
}
// No SEO plugin detected, use product name
else {
$main_keyword = $product_name;
}
return $main_keyword;
}
/**
* Call AIKTP API
*/
private function call_aiktp_api($task, $product_info) {
$api_key = $this->api_key;
if (empty($api_key)) {
return new WP_Error('no_api_key', __('API key is not configured', 'aiktp'));
}
$request_data = array(
'task' => $task,
'productInfo' => $product_info
);
$response = wp_remote_post('https://aiktp.com/api/ai.php', array(
'timeout' => 120,
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key
),
'body' => json_encode($request_data)
));
if (is_wp_error($response)) {
return $response;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
// Check for error in top level
if (isset($body['error'])) {
return new WP_Error('api_error', isset($body['error']['message']) ? $body['error']['message'] : 'API error occurred');
}
// Check for status in data field
if (isset($body['data']['status'])) {
if ($body['data']['status'] === 'success') {
// Success: return content
if (isset($body['data']['content'])) {
return $body['data']['content'];
}
// Fallback: return data if content not found
return new WP_Error('invalid_response', __('Success status but no content found', 'aiktp'));
} elseif ($body['data']['status'] === 'error') {
// Error: return error message
$error_msg = isset($body['data']['msg']) ? $body['data']['msg'] : __('API returned error status', 'aiktp');
return new WP_Error('api_error', $error_msg);
}
}
// Fallback: check for direct content field (backward compatibility)
if (!empty($body['data']['content'])) {
return $body['data']['content'];
}
// Fallback: check for direct content field at root level
if (isset($body['content'])) {
return $body['content'];
}
// Fallback: check for data field
if (isset($body['data'])) {
return is_string($body['data']) ? $body['data'] : json_encode($body['data']);
}
return new WP_Error('invalid_response', __('Invalid API response format', 'aiktp'));
}
/**
* Add custom bulk actions UI
*/
/**
* Add bulk actions to dropdown
*/
public function add_bulk_actions($actions) {
$api_key = get_option('aiktp_api_key', '');
if (!empty($api_key)) {
// Add header for AI Content SEO group
$actions['aiktpz_header'] = '↓ ' . __('AI Content SEO', 'aiktp');
// Add Gen Descriptions action
$actions['aiktpz_gen_descriptions'] = '- ' . __('Gen Product Descriptions ✨', 'aiktp');
$actions['aiktpz_gen_short_descriptions'] = '- ' . __('Gen Product Short Descriptions 🪄', 'aiktp');
}
return $actions;
}
/**
* Handle bulk actions
*/
public function handle_bulk_actions($redirect_to, $action, $post_ids) {
// Ignore header action (just for display)
if ($action === 'aiktpz_header') {
return $redirect_to;
}
// Check if it's one of our bulk actions
$type = '';
if ($action === 'aiktpz_gen_descriptions') {
$type = 'description';
} elseif ($action === 'aiktpz_gen_short_descriptions') {
$type = 'short_description';
} else {
return $redirect_to;
}
// Check if API key is configured
$api_key = get_option('aiktp_api_key', '');
if (empty($api_key)) {
$redirect_to = add_query_arg('aiktpz_bulk_error', 'no_api_key', $redirect_to);
return $redirect_to;
}
// Store product IDs and type in transient
set_transient('aiktpz_bulk_products', $post_ids, 300);
set_transient('aiktpz_bulk_type', $type, 300);
// Redirect to trigger bulk generation
$redirect_to = add_query_arg(array(
'aiktpz_bulk_generate' => '1',
'aiktpz_product_count' => count($post_ids),
'aiktpz_bulk_type' => $type,
'aiktpz_nonce' => wp_create_nonce('aiktpz_bulk_action')
), esc_url(admin_url('edit.php?post_type=product')));
return $redirect_to;
}
/**
* Add custom bulk actions UI (only for API key notice)
*/
public function add_custom_bulk_actions_ui() {
global $typenow;
if ($typenow !== 'product') {
return;
}
// Check if API key is configured
$api_key = get_option('aiktp_api_key', '');
if (empty($api_key)) {
?>
'Unauthorized'));
}
$post_id = isset($_POST['post_id']) ? absint(wp_unslash($_POST['post_id'])) : 0;
$product = wc_get_product($post_id);
if (!$product) {
wp_send_json_error(array('message' => 'Product not found: ' . $post_id));
}
// Get type from transient or default to description
$type = get_transient('aiktpz_bulk_type');
if (empty($type)) {
$type = 'description';
}
// Get product information
$product_info = $this->get_product_info($product, $post_id);
// Determine task based on type
$task = ($type === 'short_description') ? 'genProductShortDescription' : 'genProductDescription';
// Call AIKTP API
$content = $this->call_aiktp_api($task, $product_info);
if (is_wp_error($content)) {
$error_message = $content->get_error_message();
$error_data = array(
'message' => $error_message,
'product_id' => $post_id,
'product_name' => $product_info['name']
);
// Check if error is about insufficient credits
if ($error_message === 'NOT_ENOUGH_CREDITS' || strpos($error_message, 'NOT_ENOUGH_CREDITS') !== false) {
$error_data['not_enough_credits'] = true;
$error_data['message'] = __('Not enough credits to generate content. Please
purchase more credits to continue.', 'aiktp');
}
wp_send_json_error($error_data);
}
// Update product based on type
if ($type === 'short_description') {
update_post_meta($post_id, '_product_short_description', $content);
$success_message = __('Short description generated successfully!', 'aiktp');
} else {
wp_update_post(array(
'ID' => $post_id,
'post_content' => $content
));
$success_message = __('Description generated successfully!', 'aiktp');
}
wp_send_json_success(array(
'message' => $success_message,
'product_id' => $post_id,
'product_name' => $product_info['name'],
'content' => $content
));
}
/**
* AJAX get bulk products from transient
*/
public function ajax_get_bulk_products() {
check_ajax_referer('aiktpz_bulk_nonce', 'nonce');
if (!current_user_can('edit_products')) {
wp_send_json_error(array('message' => 'Unauthorized'));
}
$product_ids = get_transient('aiktpz_bulk_products');
if (!$product_ids) {
wp_send_json_error(array('message' => 'No products found'));
}
// Delete transient after retrieving
delete_transient('aiktpz_bulk_products');
wp_send_json_success(array('product_ids' => $product_ids));
}
/**
* AJAX regenerate token
*/
public function ajax_regenerate_token() {
// Verify nonce
check_ajax_referer('aiktp_regenerate_token', 'nonce');
// Check permission
if (!current_user_can('manage_options')) {
wp_send_json_error(array('message' => __('Unauthorized', 'aiktp')));
}
// Generate new token
$new_token = uniqid();
update_option('aiktpz_token', $new_token);
wp_send_json_success(array(
'token' => $new_token,
'message' => __('Token regenerated successfully', 'aiktp')
));
}
}
// Declare WooCommerce HPOS compatibility
add_action('before_woocommerce_init', function() {
if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
}
});
// Run migration on plugin load (for existing installations upgrading to new prefix)
add_action('plugins_loaded', function() {
// Check if migration has been run
if (get_option('aiktpz_migration_done') !== '1') {
AIKTPZ_Settings::migrate_old_options();
update_option('aiktpz_migration_done', '1');
}
}, 5); // Run early, before plugin initialization
// Initialize plugin
function aiktpz_init() {
// Plugin can work without WooCommerce
AIKTPZ_AI_Content_Generator::get_instance();
}
add_action('plugins_loaded', 'aiktpz_init', 20);