Changeset 3384159
- Timestamp:
- 10/24/2025 04:34:01 PM (5 months ago)
- Location:
- axiachat-ai/trunk
- Files:
-
- 5 edited
-
assets/css/aichat-frontend.css (modified) (1 diff)
-
axiachat-ai.php (modified) (5 diffs)
-
includes/class-aichat-ajax.php (modified) (6 diffs)
-
includes/contexto-functions.php (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
axiachat-ai/trunk/assets/css/aichat-frontend.css
r3376906 r3384159 395 395 396 396 .aichat-gdpr-overlay{ position:absolute; inset:0; background:rgba(0,0,0,.6); display:flex; align-items:center; justify-content:center; z-index:20; font-family:inherit; padding:1rem; } .aichat-gdpr-box{ background:#fff; color:#222; border-radius:8px; padding:1rem 1.25rem; max-width:320px; width:100%; box-shadow:0 4px 16px rgba(0,0,0,.25); font-size:14px; line-height:1.4; } .aichat-gdpr-box .aichat-gdpr-text{margin-bottom:.75rem;} .aichat-gdpr-box .aichat-gdpr-accept{ background:#0073aa; color:#fff; border:0; padding:.5rem .9rem; border-radius:4px; cursor:pointer; font-size:14px; } .aichat-gdpr-box .aichat-gdpr-accept:hover{background:#005f8a;} 397 398 /* Enlace compacto estilizado para Google Calendar (prettified server-side) */ 399 .aichat-gcal-link { 400 display: inline-flex; 401 align-items: center; 402 gap: 6px; 403 padding: 6px 10px; 404 border-radius: 6px; 405 background: #e8f0fe; /* azul muy claro */ 406 color: #1a73e8; /* azul Google-like */ 407 text-decoration: none; 408 font-weight: 600; 409 } 410 .aichat-gcal-link:hover { 411 filter: brightness(.97); 412 text-decoration: underline; 413 } -
axiachat-ai/trunk/axiachat-ai.php
r3382652 r3384159 4 4 * Plugin URI: https://wpbotwriter.com/axiachat-ai 5 5 * Description: A customizable AI chatbot for WordPress with contextual embeddings, multi‑provider support and upcoming action rules. 6 * Version: 1.2. 06 * Version: 1.2.1 7 7 * Requires at least: 5.0 8 8 * Requires PHP: 7.4 … … 20 20 21 21 // Definir constantes del plugin 22 define( 'AICHAT_VERSION', '1.2. 0' );22 define( 'AICHAT_VERSION', '1.2.1' ); 23 23 define( 'AICHAT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 24 24 define( 'AICHAT_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 25 define('AICHAT_DEBUG', true);25 define('AICHAT_DEBUG', false); 26 26 define('AICHAT_DEBUG_SYS_MAXLEN', 0); // sin truncado 27 27 … … 920 920 } 921 921 922 if ( ! function_exists('aichat_normalize_origin') ) { 923 /** 924 * Normaliza un valor origin o URL a la forma scheme://host[:port] 925 */ 926 function aichat_normalize_origin( $origin ) { 927 if ( ! $origin ) { return ''; } 928 $parts = wp_parse_url( $origin ); 929 if ( ! $parts || empty( $parts['scheme'] ) || empty( $parts['host'] ) ) { return ''; } 930 $scheme = strtolower( $parts['scheme'] ); 931 $host = strtolower( $parts['host'] ); 932 $port = isset( $parts['port'] ) ? ':' . $parts['port'] : ''; 933 return $scheme . '://' . $host . $port; 934 } 935 } 936 937 if ( ! function_exists('aichat_collect_embed_allowed_origins') ) { 938 /** 939 * Devuelve lista de origins permitidos (normalizados) combinando defaults + opción almacenada 940 */ 941 function aichat_collect_embed_allowed_origins() { 942 $defaults = []; 943 $candidates = [ get_home_url(), get_site_url() ]; 944 if ( is_multisite() ) { 945 $candidates[] = network_home_url(); 946 $candidates[] = network_site_url(); 947 } 948 foreach ( $candidates as $candidate ) { 949 $norm = aichat_normalize_origin( $candidate ); 950 if ( $norm !== '' ) { $defaults[] = $norm; } 951 } 952 953 $raw_opt = get_option( 'aichat_embed_allowed_origins', '' ); 954 if ( is_string( $raw_opt ) ) { 955 $allowed_custom = preg_split( '/\r\n|\r|\n/', $raw_opt ); 956 } else { 957 $allowed_custom = (array) $raw_opt; 958 } 959 $normalized_custom = []; 960 foreach ( $allowed_custom as $entry ) { 961 $entry = trim( (string) $entry ); 962 if ( $entry === '' ) { continue; } 963 $norm = aichat_normalize_origin( $entry ); 964 if ( $norm !== '' ) { $normalized_custom[] = $norm; } 965 } 966 967 $merged = array_unique( array_merge( $defaults, $normalized_custom ) ); 968 return apply_filters( 'aichat_embed_allowed_origins', $merged, $defaults, $normalized_custom ); 969 } 970 } 971 922 972 // ========== EMBED (Script) Nonce endpoint & origin allowlist ========== 923 973 // Simple JSON endpoint: /?aichat_embed_nonce=1 --> { nonce:"..." } … … 928 978 header('Content-Type: application/json; charset=utf-8'); 929 979 $origin = isset($_SERVER['HTTP_ORIGIN']) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_ORIGIN'] ) ) : ''; 930 $raw_opt = get_option('aichat_embed_allowed_origins', ''); 931 if (is_string($raw_opt)) { $allowed = preg_split('/\r\n|\r|\n/', $raw_opt); } else { $allowed = (array)$raw_opt; } 932 $allowed_norm = []; 933 foreach($allowed as $o){ $o = trim($o); if ($o==='') continue; $allowed_norm[] = rtrim($o,'/'); } 934 $ok = true; 935 if ($origin) { $norm_origin = rtrim($origin,'/'); if (!in_array($norm_origin,$allowed_norm,true)) { $ok = false; } } 936 if (! $ok) { echo wp_json_encode(['error'=>'origin_not_allowed']); exit; } 937 if ($origin) { header('Access-Control-Allow-Origin: '.$origin); header('Vary: Origin'); } 980 $allowed_norm = aichat_collect_embed_allowed_origins(); 981 $norm_origin = $origin ? aichat_normalize_origin( $origin ) : ''; 982 if ( $origin && ( $norm_origin === '' || ! in_array( $norm_origin, $allowed_norm, true ) ) ) { 983 echo wp_json_encode(['error'=>'origin_not_allowed']); 984 exit; 985 } 986 if ( $origin ) { 987 header('Access-Control-Allow-Origin: '.$origin); 988 header('Vary: Origin'); 989 } 938 990 939 991 $nonce = wp_create_nonce('aichat_ajax'); … … 1002 1054 } 1003 1055 1004 // Compare against site home to allow first-party even if not listed (avoid breaking admin pages served from same domain). 1005 $site_base = rtrim( get_home_url(), '/' ); 1006 $norm_origin = rtrim( $origin, '/' ); 1007 if ( strtolower($norm_origin) === strtolower($site_base) ) { 1008 // First-party; no need to consult embed allowlist. 1009 return; 1010 } 1011 1012 // Cross-origin: enforce allowlist 1013 $raw_opt = get_option('aichat_embed_allowed_origins', ''); 1014 if ( is_string($raw_opt) ) { 1015 $allowed = preg_split('/\r\n|\r|\n/', $raw_opt); 1016 } else { $allowed = (array) $raw_opt; } 1017 $allowed_norm = []; 1018 foreach ( $allowed as $o ) { 1019 $o = trim($o); 1020 if ($o === '') continue; 1021 $allowed_norm[] = rtrim($o,'/'); 1022 } 1056 $norm_origin = aichat_normalize_origin( $origin ); 1057 if ( $norm_origin === '' ) { 1058 wp_send_json_error( [ 'message' => 'Invalid origin header' ], 400 ); 1059 } 1060 1061 // Enforce allowlist (incluye defaults por dominio actual) 1062 $allowed_norm = aichat_collect_embed_allowed_origins(); 1023 1063 if ( ! in_array( $norm_origin, $allowed_norm, true ) ) { 1024 1064 wp_send_json_error( [ 'message' => 'Embedding origin not allowed' ], 403 ); -
axiachat-ai/trunk/includes/class-aichat-ajax.php
r3382652 r3384159 251 251 if ( ! empty( $intercept['abort'] ) ) { 252 252 $answer = isset($intercept['immediate_response']) ? (string)$intercept['immediate_response'] : __( 'No response available.', 'axiachat-ai' ); 253 $answer = $this->sanitize_answer_html( aichat_replace_link_placeholder( $answer ) ); 253 // Reemplazo [LINK] y embellecer enlaces conocidos antes de sanear 254 $answer = aichat_replace_link_placeholder( $answer ); 255 if ( function_exists('aichat_pretty_known_links') ) { $answer = aichat_pretty_known_links( $answer ); } 256 $answer = $this->sanitize_answer_html( $answer ); 254 257 if ( get_option( 'aichat_logging_enabled', 1 ) ) { 255 258 $this->maybe_log_conversation( get_current_user_id(), $session, $bot['slug'], $page_id, $message, $answer, $model, $provider, null, null, null, null ); … … 340 343 } 341 344 // Sanitizar y log opcional 342 $answer = $this->sanitize_answer_html( aichat_replace_link_placeholder( $answer ) ); 345 $answer = aichat_replace_link_placeholder( $answer ); 346 if ( function_exists('aichat_pretty_known_links') ) { $answer = aichat_pretty_known_links( $answer ); } 347 $answer = $this->sanitize_answer_html( $answer ); 343 348 if ( get_option( 'aichat_logging_enabled', 1 ) ) { 344 349 $this->maybe_log_conversation( get_current_user_id(), $session, $bot_slug, $page_id, $message, $answer, $model, $provider, null, null, null, null ); … … 636 641 } 637 642 638 // 6) Reemplazo [LINK] 643 // 6) Reemplazo [LINK] y embellecer enlaces conocidos 639 644 $answer = aichat_replace_link_placeholder( $answer ); 645 if ( function_exists('aichat_pretty_known_links') ) { 646 $answer = aichat_pretty_known_links( $answer ); 647 } 640 648 641 649 // 6.1) Sanitizar HTML permitido (permitimos <a>, <strong>, <em>, listas, etc.) … … 974 982 } 975 983 $final_text = aichat_replace_link_placeholder( $final_text ); 984 if ( function_exists('aichat_pretty_known_links') ) { $final_text = aichat_pretty_known_links( $final_text ); } 976 985 $final_text = $this->sanitize_answer_html( $final_text ); 977 986 … … 1385 1394 if ($answer === '') return new WP_Error('aichat_empty_answer','Empty answer'); 1386 1395 $answer = aichat_replace_link_placeholder( $answer ); 1396 // Convert known plain URLs (e.g., Google Calendar) into compact, safe anchors 1397 if ( function_exists('aichat_pretty_known_links') ) { 1398 $answer = aichat_pretty_known_links( $answer ); 1399 } 1387 1400 $answer = $this->sanitize_answer_html( $answer ); 1388 1401 … … 1567 1580 protected function sanitize_answer_html( $html ) { 1568 1581 $allowed = [ 1569 'a' => [ 'href' => true, 'target' => true, 'rel' => true, 'title' => true ],1582 'a' => [ 'href' => true, 'target' => true, 'rel' => true, 'title' => true, 'class' => true ], 1570 1583 'strong' => [], 'em' => [], 'b' => [], 'i' => [], 1571 1584 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [], -
axiachat-ai/trunk/includes/contexto-functions.php
r3382652 r3384159 814 814 return $out; 815 815 } 816 817 /** 818 * Reemplaza URLs conocidas en texto plano por enlaces compactos y seguros. 819 * Por ahora: Google Calendar "render?action=TEMPLATE". 820 * 821 * - Solo actúa sobre texto plano; si detecta etiquetas <a>, no modifica para evitar anidar anchors. 822 * - Allowlist estricto por dominio y ruta. 823 * - Devuelve el mismo texto si no hay coincidencias. 824 */ 825 function aichat_pretty_known_links( $text ){ 826 $s = (string)$text; 827 if ($s === '') return $s; 828 // Si ya hay anchors, ser conservadores y no modificar 829 if (stripos($s, '<a') !== false) return $s; 830 831 // Buscar URLs de Google Calendar render 832 $label = function_exists('apply_filters') ? apply_filters('aichat_gcal_link_label', __('Google Calendar','axiachat-ai')) : __('Google Calendar','axiachat-ai'); 833 $pattern = '/\bhttps:\/\/calendar\.google\.com\/calendar\/render\?[^\s<]+/i'; 834 $s = preg_replace_callback($pattern, function($m) use ($label){ 835 $url = $m[0]; 836 // Validación adicional: requerir action=TEMPLATE en query 837 if (stripos($url, 'action=TEMPLATE') === false) { return $url; } 838 // Componer anchor con target seguro 839 $href = esc_url($url); 840 $title = esc_attr__('Add to Google Calendar','axiachat-ai'); 841 return '<a class="aichat-gcal-link" href="'.$href.'" target="_blank" rel="noopener nofollow" title="'.$title.'">'.esc_html($label).'</a>'; 842 }, $s); 843 844 return $s; 845 } -
axiachat-ai/trunk/readme.txt
r3382652 r3384159 1 === AxiaChat AI ===1 === AxiaChat AI – Free AI Chatbot for WordPress (Answers Customers Automatically) === 2 2 Contributors: estebandezafra 3 3 Tags: chatbot, ai, openai, chat, assistant … … 5 5 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 1.2. 07 Stable tag: 1.2.1 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html
Note: See TracChangeset
for help on using the changeset viewer.