Plugin Directory

Changeset 3384159


Ignore:
Timestamp:
10/24/2025 04:34:01 PM (5 months ago)
Author:
estebandezafra
Message:

update ver 1.2.1

Location:
axiachat-ai/trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • axiachat-ai/trunk/assets/css/aichat-frontend.css

    r3376906 r3384159  
    395395
    396396.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  
    44 * Plugin URI:        https://wpbotwriter.com/axiachat-ai
    55 * Description:       A customizable AI chatbot for WordPress with contextual embeddings, multi‑provider support and upcoming action rules.
    6  * Version:           1.2.0
     6 * Version:           1.2.1
    77 * Requires at least: 5.0
    88 * Requires PHP:      7.4
     
    2020
    2121// Definir constantes del plugin
    22 define( 'AICHAT_VERSION', '1.2.0' );
     22define( 'AICHAT_VERSION', '1.2.1' );
    2323define( 'AICHAT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    2424define( 'AICHAT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    25 define('AICHAT_DEBUG', true);
     25define('AICHAT_DEBUG', false);
    2626define('AICHAT_DEBUG_SYS_MAXLEN', 0); // sin truncado
    2727
     
    920920}
    921921
     922if ( ! 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
     937if ( ! 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
    922972// ========== EMBED (Script) Nonce endpoint & origin allowlist ==========
    923973// Simple JSON endpoint: /?aichat_embed_nonce=1  --> { nonce:"..." }
     
    928978  header('Content-Type: application/json; charset=utf-8');
    929979  $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  }
    938990
    939991  $nonce = wp_create_nonce('aichat_ajax');
     
    10021054  }
    10031055
    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();
    10231063  if ( ! in_array( $norm_origin, $allowed_norm, true ) ) {
    10241064    wp_send_json_error( [ 'message' => 'Embedding origin not allowed' ], 403 );
  • axiachat-ai/trunk/includes/class-aichat-ajax.php

    r3382652 r3384159  
    251251            if ( ! empty( $intercept['abort'] ) ) {
    252252                $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 );
    254257                if ( get_option( 'aichat_logging_enabled', 1 ) ) {
    255258                    $this->maybe_log_conversation( get_current_user_id(), $session, $bot['slug'], $page_id, $message, $answer, $model, $provider, null, null, null, null );
     
    340343                }
    341344                // 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 );
    343348                if ( get_option( 'aichat_logging_enabled', 1 ) ) {
    344349                    $this->maybe_log_conversation( get_current_user_id(), $session, $bot_slug, $page_id, $message, $answer, $model, $provider, null, null, null, null );
     
    636641            }
    637642
    638             // 6) Reemplazo [LINK]
     643            // 6) Reemplazo [LINK] y embellecer enlaces conocidos
    639644            $answer = aichat_replace_link_placeholder( $answer );
     645            if ( function_exists('aichat_pretty_known_links') ) {
     646                $answer = aichat_pretty_known_links( $answer );
     647            }
    640648
    641649            // 6.1) Sanitizar HTML permitido (permitimos <a>, <strong>, <em>, listas, etc.)
     
    974982            }
    975983            $final_text = aichat_replace_link_placeholder( $final_text );
     984            if ( function_exists('aichat_pretty_known_links') ) { $final_text = aichat_pretty_known_links( $final_text ); }
    976985            $final_text = $this->sanitize_answer_html( $final_text );
    977986
     
    13851394            if ($answer === '') return new WP_Error('aichat_empty_answer','Empty answer');
    13861395            $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            }
    13871400            $answer = $this->sanitize_answer_html( $answer );
    13881401
     
    15671580        protected function sanitize_answer_html( $html ) {
    15681581            $allowed = [
    1569                 'a'      => [ 'href' => true, 'target' => true, 'rel' => true, 'title' => true ],
     1582                'a'      => [ 'href' => true, 'target' => true, 'rel' => true, 'title' => true, 'class' => true ],
    15701583                'strong' => [], 'em' => [], 'b' => [], 'i' => [],
    15711584                'br'     => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [],
  • axiachat-ai/trunk/includes/contexto-functions.php

    r3382652 r3384159  
    814814    return $out;
    815815}
     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 */
     825function 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) ===
    22Contributors: estebandezafra
    33Tags: chatbot, ai, openai, chat, assistant
     
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.2.0
     7Stable tag: 1.2.1
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
Note: See TracChangeset for help on using the changeset viewer.