= $get_limt) { $expiration_date = get_transient('wpwand_pgc_limit_expiration'); if (!$expiration_date || current_time('timestamp') > $expiration_date) { $expiration_date = current_time('timestamp') + 30 * DAY_IN_SECONDS; set_transient('wpwand_pgc_limit_expiration', $expiration_date, 30 * DAY_IN_SECONDS); update_option('wpwand_pgc_total_bulk_generated', 0); return true; } return false; } return true; } } if (!function_exists('wpwand_templates')) { /** * Legacy template/prompt catalog: free + pro + custom, merged. Side-effect-free * (the legacy transient-sync calls were dropped — the 2.0.0 data layer owns seeding). */ function wpwand_templates() { $all_prompts = get_option('wpwand_data'); $custom_data = get_option('wpwand_custom_data', []); // One list now. Templates::flatten() still reads the old {free, pro} shape, which is what a // Pro licence sync writes into this option. return array_merge((array) $custom_data, \WPWand\Data\Templates::flatten($all_prompts)); } } if (!function_exists('wpwand_ai_error')) { /** * Legacy provider-error renderer. Verbatim port (self-contained, no removed deps). * * @param object $error stdClass with ->type and ->message (message may be JSON). */ function wpwand_ai_error($error) { $source = str_replace('_error', '', isset($error->type) ? $error->type : ''); $provider = ucfirst($source); // Anchored and case-insensitive: WP_Http writes "cURL error 28: …", so the old // strpos($msg, 'curl') never matched, and an unanchored match would also have claimed any // provider sentence that merely mentions curl. Same gate as ErrorFormatter::transport_message(). if (isset($error->message) && preg_match('/^cURL error \d+/i', (string) $error->message)) { return "

{$provider} Error

" . esc_html__('Server is not responding. Please try again later.', 'ai-content-generation') . "

"; } $error_details = isset($error->message) ? json_decode($error->message) : null; if (json_last_error() === JSON_ERROR_NONE && is_object($error_details)) { $message = "

{$provider} Error

"; if (isset($error_details->type)) { $message .= "

Type: " . ucwords(str_replace('_', ' ', htmlspecialchars($error_details->type))) . "

"; } if (isset($error_details->message)) { $message_text = htmlspecialchars($error_details->message); $message_text = preg_replace( '/(https?:\/\/[^\s]+)/', '$1', $message_text ); $message .= "
" . $message_text . "
"; } return $message; } return "

{$provider} Error

" . htmlspecialchars(isset($error->message) ? $error->message : '') . "
"; } } if (!function_exists('wpwand_api_fields_validate')) { /** * Legacy AJAX field sanitizer. Verbatim port. * * phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce IS checked below. */ function wpwand_api_fields_validate() { if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'wpwand_global_nonce')) { wp_send_json_error('Nonce verification failed.', 403); } return array( 'topic' => isset($_POST['topic']) ? sanitize_text_field(wp_unslash($_POST['topic'])) : '', 'keywords' => isset($_POST['keyword']) ? sanitize_text_field(wp_unslash($_POST['keyword'])) : '', 'no_of_results' => isset($_POST['result_number']) ? absint(sanitize_text_field(wp_unslash($_POST['result_number']))) : 1, 'tone' => isset($_POST['tone']) ? sanitize_text_field(wp_unslash($_POST['tone'])) : '', 'word_count' => isset($_POST['word_limit']) ? intval(sanitize_text_field(wp_unslash($_POST['word_limit']))) + 1000 : '', 'product_name' => isset($_POST['product_name']) ? sanitize_text_field(wp_unslash($_POST['product_name'])) : '', 'description' => isset($_POST['description']) ? sanitize_text_field(wp_unslash($_POST['description'])) : '', 'content' => isset($_POST['content']) ? wp_kses_post(sanitize_text_field(wp_unslash($_POST['content']))) : '', 'content_textarea' => isset($_POST['content_textarea']) ? wp_kses_post(sanitize_text_field(wp_unslash($_POST['content_textarea']))) : '', 'custom_textarea' => isset($_POST['custom_textarea']) ? wp_kses_post(sanitize_text_field(wp_unslash($_POST['custom_textarea']))) : '', 'product_1' => isset($_POST['product_1']) ? wp_kses_post(sanitize_text_field(wp_unslash($_POST['product_1']))) : '', 'product_2' => isset($_POST['product_2']) ? wp_kses_post(sanitize_text_field(wp_unslash($_POST['product_2']))) : '', 'description_1' => isset($_POST['description_1']) ? wp_kses_post(sanitize_text_field(wp_unslash($_POST['description_1']))) : '', 'description_2' => isset($_POST['description_2']) ? wp_kses_post(sanitize_text_field(wp_unslash($_POST['description_2']))) : '', 'subject' => isset($_POST['subject']) ? sanitize_text_field(wp_unslash($_POST['subject'])) : '', 'question' => isset($_POST['question']) ? sanitize_text_field(wp_unslash($_POST['question'])) : '', 'comment' => isset($_POST['comment']) ? sanitize_text_field(wp_unslash($_POST['comment'])) : '', ); // phpcs:enable WordPress.Security.NonceVerification.Missing } } if (!function_exists('wpwand_generate_ai_content')) { /** * Legacy generation entrypoint. The free 2.0.0 code never calls this — only legacy Pro * does — so it doubles as the Pro feature lock: while Pro is outdated it returns an * "update required" error (in the new Generator's error shape) instead of generating. * Once Pro is on 2.0.0 it calls the Generator directly and this shim is never hit. * * @return object stdClass with ->choices or ->error (legacy-identical shape). */ function wpwand_generate_ai_content($prompt, $number = 1, $args = []) { if (wpwand_legacy_pro_is_outdated()) { return (object) [ 'error' => (object) [ 'message' => wp_json_encode([ 'type' => 'update_required', 'message' => __('Please update WP Wand Pro to version 2.0.0 or higher to keep generating content. Your Pro features are paused until the update completes.', 'ai-content-generation'), ]), 'type' => 'wpwand_error', 'code' => 426, ], ]; } if (class_exists('WPWand\\Generation\\Generator')) { return \WPWand\Generation\Generator::generate((string) $prompt, (int) $number, (array) $args); } return (object) [ 'error' => (object) [ 'message' => wp_json_encode(['message' => __('The generation engine is unavailable.', 'ai-content-generation')]), 'type' => 'wpwand_error', 'code' => 500, ], ]; } } if (!function_exists('wpwand_loago_icon_url')) { /** * Legacy brand/logo icon URL. Faithful port (depends only on the shimmed option getter). */ function wpwand_loago_icon_url() { $logo = wpwand_get_option('wpwand_logo_icon'); $is_agency = wpwand_get_option('wpwand_pro_tala_agency'); if (!empty($logo) && $is_agency && function_exists('wpwand_pro_init')) { return $logo; } return defined('WPWAND_PLUGIN_URL') ? WPWAND_PLUGIN_URL . 'assets/img/icon.svg' : plugins_url('assets/img/icon.svg', dirname(__DIR__) . '/wp-wand.php'); } } if (!function_exists('wpwand_get_data')) { /** * Legacy template-data (re)seed. Only the legacy Pro license-deactivate handler calls * this; it re-seeds the free catalog (the legacy wpwand_dummy_datas() source is gone, so * this routes through the 2.0.0 data layer instead). */ function wpwand_get_data($sync = false) { if (!get_option('wpwand_data') || true === $sync) { if (class_exists('WPWand\\Data\\Templates')) { \WPWand\Data\Templates::seed(true); return true; } return false; } return []; } } if (!function_exists('wpwand_dall_e_request')) { /** * Legacy DALL·E image generation. Only legacy Pro calls this; image generation is a Pro * feature, so while Pro is outdated it is locked: respond with an update notice (matching * the legacy wp_send_json HTML-string contract) instead of calling the image API. */ function wpwand_dall_e_request($prompt, $args = []) { $message = '

' . esc_html__('Image generation is paused until WP Wand Pro is updated to version 2.0.0 or higher.', 'ai-content-generation') . '

'; wp_send_json($message); } } if (!function_exists('wpwand_pro_card')) { /** * Legacy Pro upsell card. Every legacy call site is commented out, so this is a * defensive no-op kept only so an uncommented call can never fatal. */ function wpwand_pro_card() { return ''; } }