/** * AJAX: Regenerar SOLO SCHEMA (robusto, sin pedir JSON-LD completo al modelo) */ add_action( 'wp_ajax_nv_gemini_regen_schema', function() { // Base: permisos + nonce + función request + rate-limit (usa tu helper existente) $post = nv_gemini_check_ajax_base( 'nv_gemini_generate_save' ); $post_id = $post->ID; // Prompt adicional $prompt = isset($_POST['prompt']) ? sanitize_textarea_field( wp_unslash($_POST['prompt']) ) : ''; if ( empty( $prompt ) ) { wp_send_json_error([ 'message' => 'Prompt vacío para schema.' ]); } // Título y contenido $title = get_post_meta( $post_id, 'rank_math_title', true ); if ( empty( $title ) ) $title = get_the_title( $post_id ); $content_plain = wp_strip_all_tags( $post->post_content ); if ( function_exists('mb_substr') ) { $content_plain = mb_substr( $content_plain, 0, 2000 ); } else { $content_plain = substr( $content_plain, 0, 2000 ); } // ===================================================== // 1) Prompt “simple JSON” (NO JSON-LD completo) // Esto reduce mucho las respuestas vacías. [1](https://cvefeed.io/vuln/detail/CVE-2022-47161) // ===================================================== $instruction = "Extrae SOLO estos campos para un artículo SEO en español. Devuélvelos como un objeto JSON SIMPLE (sin @context, sin @type, sin JSON-LD completo). NO incluyas texto fuera del JSON. Claves requeridas: - headline: título del artículo (string) - description: resumen SEO en español (máx 160 caracteres) - author_name: nombre del autor (string, si no se indica usa 'Redacción') - date_published: fecha en formato YYYY-MM-DD (si no se conoce, usa la de hoy) TÍTULO: {$title} CONTENIDO: {$content_plain} PROMPT ADICIONAL: {$prompt}"; // Usa caché por prompt si tienes nv_gemini_cached_request(), si no usa nv_gemini_pro_request() if ( function_exists('nv_gemini_cached_request') ) { $raw = nv_gemini_cached_request( $instruction, 24 ); } else { $raw = nv_gemini_pro_request( $instruction ); } if ( empty( $raw ) ) { // generateContent puede devolver respuesta vacía por filtros/otros motivos sin error HTTP. [1](https://cvefeed.io/vuln/detail/CVE-2022-47161) wp_send_json_error([ 'message' => 'No devuelve contenido para schema (API Key, timeout o bloqueo).' ]); } // ===================================================== // 2) Extraer JSON robustamente // ===================================================== if ( function_exists('nv_extract_json_object') ) { $clean = nv_extract_json_object( $raw ); } else { // fallback mínimo si no existe tu helper $raw2 = preg_replace('/^```(?:json)?/i', '', trim($raw)); $raw2 = preg_replace('/```$/', '', trim($raw2)); $start = strpos($raw2, '{'); $end = strrpos($raw2, '}'); $clean = ($start !== false && $end !== false && $end > $start) ? substr($raw2, $start, $end - $start + 1) : ''; } if ( empty($clean) ) { error_log('[NV Gemini] Schema-only: no se pudo extraer JSON. Raw: ' . substr($raw,0,500)); wp_send_json_error([ 'message' => 'Respuesta sin JSON utilizable para schema.' ]); } $simple = json_decode( $clean, true ); if ( ! is_array( $simple ) ) { error_log('[NV Gemini] Schema-only: JSON inválido. Clean: ' . substr($clean,0,500)); wp_send_json_error([ 'message' => 'La respuesta de Gemini para schema no es JSON válido.' ]); } // ===================================================== // 3) Construir JSON-LD final en PHP (estable) // ===================================================== $headline = sanitize_text_field( $simple['headline'] ?? $title ); $desc = sanitize_text_field( $simple['description'] ?? '' ); $author = sanitize_text_field( $simple['author_name'] ?? 'Redacción' ); $date_pub = sanitize_text_field( $simple['date_published'] ?? date('Y-m-d') ); // Recortar description por seguridad if ( function_exists('mb_substr') ) { $desc = mb_substr( $desc, 0, 160 ); } else { $desc = substr( $desc, 0, 160 ); } // Validar fecha (muy básico) if ( ! preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_pub ) ) { $date_pub = date('Y-m-d'); } $schema_ld = [ '@context' => 'https://schema.org', '@type' => 'Article', 'headline' => $headline, 'description' => $desc, 'author' => [ '@type' => 'Person', 'name' => $author, ], 'datePublished' => $date_pub, 'inLanguage' => 'es-ES', 'mainEntityOfPage' => get_permalink( $post_id ), ]; // ===================================================== // 4) Guardar schema raw (para inyectar en wp_head) // ===================================================== if ( function_exists('nv_gemini_save_schema_raw') ) { nv_gemini_save_schema_raw( $post_id, $schema_ld ); } else { update_post_meta( $post_id, 'nv_gemini_schema_raw', wp_json_encode( $schema_ld ) ); } wp_send_json_success([ 'message' => 'Schema guardado (Article). Se inyectará en el head si está activada la inyección.' ]); });