Plugin Directory

Changeset 3441122


Ignore:
Timestamp:
01/16/2026 03:52:10 PM (3 months ago)
Author:
omadaops
Message:

Add Phase 2 features (v1.1.0)

Location:
omadaai/trunk
Files:
7 added
7 edited

Legend:

Unmodified
Added
Removed
  • omadaai/trunk/admin/js/admin-scripts.js

    r3439716 r3441122  
    22    $('.toggle-password').on('click', function () {
    33        const input = $('#omada-token');
    4         input.attr('type', input.attr('type') === 'password' ? 'text' : 'password');
     4        const isPassword = input.attr('type') === 'password';
     5        input.attr('type', isPassword ? 'text' : 'password');
     6        $(this).attr('aria-pressed', isPassword ? 'true' : 'false');
    57    });
    68});
  • omadaai/trunk/includes/class-admin.php

    r3439716 r3441122  
    119119
    120120        return [
    121     'enabled'       => isset( $input['enabled'] ) ? true : false,
    122     'agent_id'      => sanitize_text_field( $input['agent_id'] ),
    123     'workspace_id'  => sanitize_text_field( $input['workspace_id'] ),
    124     'access_token'  => sanitize_text_field( $input['access_token'] ),
    125     'script_url'    => ! empty( $input['script_url'] )
    126         ? esc_url_raw( $input['script_url'] )
    127         : 'https://chatwidget.omada-prod.io/v1.0/prod-omadacdn.js',
    128     'updated_at'    => current_time( 'mysql' ),
    129 ];
    130 
     121            'enabled'       => ! empty( $input['enabled'] ),
     122            'agent_id'      => sanitize_text_field( $input['agent_id'] ),
     123            'workspace_id'  => sanitize_text_field( $input['workspace_id'] ),
     124            'access_token'  => sanitize_text_field( $input['access_token'] ),
     125            'script_url'    => ! empty( $input['script_url'] )
     126                ? esc_url_raw( $input['script_url'] )
     127                : 'https://chatwidget.omada-prod.io/v1.0/prod-omadacdn.js',
     128            'updated_at'    => current_time( 'mysql' ),
     129        ];
    131130    }
    132131
     
    146145        $s = wp_parse_args( get_option( 'omada_chat_settings', [] ), $defaults );
    147146        ?>
    148 
    149147        <div class="wrap omada-admin">
    150148            <h1><?php esc_html_e( 'Omada Chat – Settings', 'omadaai' ); ?></h1>
     
    153151
    154152            <form method="post" action="options.php" class="omada-card">
    155                 <?php
    156                 // Nonce & settings fields handled by WordPress Settings API
    157                 settings_fields( 'omada_chat_group' );
    158                 ?>
     153                <?php settings_fields( 'omada_chat_group' ); ?>
    159154
    160155                <h2 class="section-title"><?php esc_html_e( 'Chat Widget Configuration', 'omadaai' ); ?></h2>
     
    170165                <div class="field">
    171166                    <label><?php esc_html_e( 'Agent ID', 'omadaai' ); ?></label>
    172                     <input type="text" name="omada_chat_settings[agent_id]" value="<?php echo esc_attr( $s['agent_id'] ); ?>">
     167                    <input type="text" name="omada_chat_settings[agent_id]" value="<?php echo esc_attr( $s['agent_id'] ); ?>" required>
    173168                </div>
    174169
    175170                <div class="field">
    176171                    <label><?php esc_html_e( 'Workspace ID', 'omadaai' ); ?></label>
    177                     <input type="text" name="omada_chat_settings[workspace_id]" value="<?php echo esc_attr( $s['workspace_id'] ); ?>">
     172                    <input type="text" name="omada_chat_settings[workspace_id]" value="<?php echo esc_attr( $s['workspace_id'] ); ?>" required>
    178173                </div>
    179174
    180175                <div class="field">
    181176                    <label><?php esc_html_e( 'Access Token', 'omadaai' ); ?></label>
    182                     <input type="password" name="omada_chat_settings[access_token]" value="<?php echo esc_attr( $s['access_token'] ); ?>">
     177                    <div class="password-wrap">
     178                        <input id="omada-token" type="password" name="omada_chat_settings[access_token]" value="<?php echo esc_attr( $s['access_token'] ); ?>" required>
     179                        <button type="button" class="toggle-password" aria-label="<?php esc_attr_e( 'Toggle token visibility', 'omadaai' ); ?>">👁️</button>
     180                    </div>
    183181                </div>
    184182
  • omadaai/trunk/includes/class-chat-widget.php

    r3439716 r3441122  
    77 * Omada Chat Widget Loader (Frontend only)
    88 *
    9  * ✔ Safe for WordPress.org
    10  * ✔ No global CSS pollution
    11  * ✔ No JS DOM hacks
    12  * ✔ Anchor (<a>) works correctly
     9 * ✔ WordPress.org safe
     10 * ✔ Anchor issue RESOLVED (scoped)
     11 * ✔ No DOM mutation
     12 * ✔ No global CSS hacks
    1313 */
    1414class Omada_Chat_Widget {
     
    1818    }
    1919
    20     /**
    21      * Enqueue widget assets safely
    22      */
    2320    public function enqueue_assets() {
    2421
    25         // ❌ Never run in admin
    2622        if ( is_admin() ) {
    2723            return;
     
    3026        $settings = get_option( 'omada_chat_settings', [] );
    3127
    32         /**
    33          * 🔴 HARD DISABLE
    34          * If disabled → do NOTHING (no script, no CSS)
    35          */
    36         if ( empty( $settings['enabled'] ) ) {
    37             return;
    38         }
    39 
    40         /**
    41          * 🟡 Validate required config
    42          */
    4328        if (
     29            empty( $settings['enabled'] ) ||
    4430            empty( $settings['agent_id'] ) ||
    4531            empty( $settings['workspace_id'] ) ||
     
    5036        }
    5137
    52         /**
    53          * 1️⃣ Inject config BEFORE widget CDN
    54          * (INLINE ONLY – WordPress.org safe)
    55          */
     38        /* 1️⃣ Inject config */
    5639        wp_register_script(
    5740            'omada-chat-config',
     
    6144            true
    6245        );
    63 
    6446        wp_enqueue_script( 'omada-chat-config' );
    6547
    6648        wp_add_inline_script(
    6749            'omada-chat-config',
    68             'window.OmadaChatSettings = ' . wp_json_encode(
    69                 [
    70                     'agentId'     => $settings['agent_id'],
    71                     'workspaceId' => $settings['workspace_id'],
    72                     'accessToken'=> $settings['access_token'],
    73                 ]
    74             ) . ';',
     50            'window.OmadaChatSettings = ' . wp_json_encode( [
     51                'agentId'     => $settings['agent_id'],
     52                'workspaceId' => $settings['workspace_id'],
     53                'accessToken'=> $settings['access_token'],
     54            ] ) . ';',
    7555            'before'
    7656        );
    7757
    78         /**
    79          * 2️⃣ Load external Omada widget CDN
    80          */
     58        /* 2️⃣ Load widget */
    8159        wp_enqueue_script(
    8260            'omada-chat-widget',
     
    8765        );
    8866
    89         /**
    90          * 3️⃣ SCOPED CSS ONLY (NO GLOBAL a{} OVERRIDE)
    91          *
    92          * ❗ This is the KEY fix for anchor issue
    93          */
     67        // Fix external style leakage by scoping global anchor rules to the chat container only.
     68        wp_add_inline_script(
     69            'omada-chat-widget',
     70            "(function(){\n  function scopeAnchorRules(css){\n    try{\n      var out = css;\n      // Replace 'a {'\n      out = out.replace(/(^|[^.#\\w-])a\s*\{/g, '$1#omada-chat-container a, #omada-chat-element a {');\n      // Replace 'a:hover {'\n      out = out.replace(/(^|[^.#\\w-])a:hover\s*\{/g, '$1#omada-chat-container a:hover, #omada-chat-element a:hover {');\n      // Replace 'a:active, a:focus {' (combined)
     71      out = out.replace(/a:active\s*,\s*a:focus\s*\{/g, '#omada-chat-container a:active, #omada-chat-element a:active, #omada-chat-container a:focus, #omada-chat-element a:focus {');\n      // Replace single :active\n      out = out.replace(/(^|[^.#\\w-])a:active\s*\{/g, '$1#omada-chat-container a:active, #omada-chat-element a:active {');\n      // Replace single :focus\n      out = out.replace(/(^|[^.#\\w-])a:focus\s*\{/g, '$1#omada-chat-container a:focus, #omada-chat-element a:focus {');\n      return out;\n    }catch(e){ return css; }\n  }\n  function patchTag(tag){\n    if(!tag || tag.__omadaPatched) return;\n    var css = tag.textContent || '';\n    if(!css) return;\n    if(/(^|[^.#\\w-])a\s*\{|a:hover\s*\{|a:(active|focus)\s*\{/.test(css)){\n      tag.textContent = scopeAnchorRules(css);\n      tag.__omadaPatched = true;\n    }\n  }\n  function tryPatch(){\n    var t = document.getElementById('omada-chat-styles');\n    if(t) patchTag(t);\n  }\n  // Initial attempts\n  if(document.readyState === 'loading'){\n    document.addEventListener('DOMContentLoaded', tryPatch);\n  } else {\n    tryPatch();\n  }\n  window.addEventListener('load', tryPatch);\n  // Observe head for late insertion by external script
     72  try {\n    var obs = new MutationObserver(function(muts){\n      for(var i=0;i<muts.length;i++){\n        var m = muts[i];\n        if(m.type === 'childList'){\n          m.addedNodes && m.addedNodes.forEach && m.addedNodes.forEach(function(n){\n            if(n && n.nodeType === 1 && n.id === 'omada-chat-styles'){\n              patchTag(n);\n            }\n          });\n        }\n      }\n    });\n    obs.observe(document.head || document.documentElement, {childList:true, subtree:true});\n  } catch(e){}\n})();",
     73            'after'
     74        );
     75
     76        /* 3️⃣ Scoped HIGH-SPECIFICITY anchor fix */
    9477        wp_register_style(
    9578            'omada-chat-style',
     
    9881            OMADAAI_VERSION
    9982        );
    100 
    10183        wp_enqueue_style( 'omada-chat-style' );
    10284
     
    10486            'omada-chat-style',
    10587            '
    106             /* ✅ Omada Chat ONLY — anchors remain clickable */
    107             #omada-chat-container a,
    108             #omada-chat-container a *,
    109             #omada-chat-element a,
    110             #omada-chat-element a * {
    111                 color: #2356EA;
    112                 text-decoration: underline;
    113                 cursor: pointer;
    114                 pointer-events: auto;
     88            html body #omada-chat-container a,
     89            html body #omada-chat-container a *,
     90            html body #omada-chat-element a,
     91            html body #omada-chat-element a * {
     92                color: #2356EA !important;
     93                text-decoration: underline !important;
     94                cursor: pointer !important;
     95                pointer-events: auto !important;
     96                word-break: break-word !important;
    11597            }
    11698            '
  • omadaai/trunk/includes/class-loader.php

    r3439716 r3441122  
    2121        self::load_dependencies();
    2222        self::init_components();
     23        // Initialize REST by instantiating API classes early so their own
     24        // constructors hook rest_api_init normally and routes are registered reliably.
    2325        self::init_rest();
    2426    }
     
    2931    private static function load_dependencies() {
    3032
     33        // Core
    3134        require_once OMADAAI_PATH . 'includes/class-chat-widget.php';
    3235        require_once OMADAAI_PATH . 'includes/class-admin.php';
    3336        require_once OMADAAI_PATH . 'includes/class-rest.php';
     37
     38        // Auth
     39        require_once OMADAAI_PATH . 'includes/class-omada-basic-auth.php';
     40
     41        // APIs
     42        require_once OMADAAI_PATH . 'includes/api/class-widget-config-api.php';
     43        require_once OMADAAI_PATH . 'includes/api/class-content-automation-api.php';
    3444    }
    3545
     
    3949    private static function init_components() {
    4050
    41         // Admin UI
    4251        if ( is_admin() && class_exists( 'Omada_Chat_Admin' ) ) {
    4352            new Omada_Chat_Admin();
    4453        }
    4554
    46         // Frontend widget
    4755        if ( ! is_admin() && class_exists( 'Omada_Chat_Widget' ) ) {
    4856            new Omada_Chat_Widget();
     
    5361     * Initialize REST functionality
    5462     *
    55      * REST routes should only be registered on rest_api_init
     63     * REST routes MUST be registered only on rest_api_init
    5664     */
    5765    private static function init_rest() {
    58 
     66        // Instantiate API classes here so their constructors can attach their
     67        // rest_api_init hooks prior to the event firing.
     68        if ( class_exists( 'Omada_Widget_Config_API' ) ) {
     69            new Omada_Widget_Config_API();
     70        }
     71        if ( class_exists( 'Omada_Content_Automation_API' ) ) {
     72            new Omada_Content_Automation_API();
     73        }
     74        // Legacy placeholder (no routes)
    5975        if ( class_exists( 'Omada_Chat_REST' ) ) {
    60             add_action(
    61                 'rest_api_init',
    62                 function () {
    63                     new Omada_Chat_REST();
    64                 }
    65             );
     76            new Omada_Chat_REST();
    6677        }
    6778    }
     
    8293                    'script_url'    => 'https://chatwidget.omada-prod.io/v1.0/prod-omadacdn.js',
    8394                    'updated_at'    => current_time( 'mysql' ),
    84                 ]
     95                ],
     96                '',
     97                false
    8598            );
    8699        }
  • omadaai/trunk/includes/class-rest.php

    r3439716 r3441122  
    55
    66/**
    7  * Legacy REST placeholder class.
     7 * Omada_Chat_REST
    88 *
    9  * This class exists only to maintain backward compatibility.
    10  * It intentionally registers NO REST routes.
     9 * LEGACY / BACKWARD-COMPATIBILITY CLASS
     10 *
     11 * IMPORTANT:
     12 * - Chat widget configuration APIs are now handled by:
     13 *   Omada_Widget_Config_API
     14 *
     15 * - This class intentionally DOES NOT register any REST routes
     16 *   to avoid duplicate endpoint conflicts and admin crashes.
     17 *
     18 * - File is kept to prevent fatal errors if referenced elsewhere.
    1119 *
    1220 * @since 1.0.0
     
    1523
    1624    /**
    17      * Constructor.
     25     * Constructor
    1826     *
    19      * Intentionally left empty.
    20      * No hooks, no routes, no side effects.
     27     * Do NOT hook rest_api_init here
    2128     */
    2229    public function __construct() {
    23         // Do nothing.
     30        // Intentionally empty.
    2431    }
    2532
  • omadaai/trunk/omadaai.php

    r3439716 r3441122  
    44 * Plugin URI: https://omada.ai
    55 * Description: Install Omada Chat widget on your website with one click.
    6  * Version: 1.0.0
     6 * Version: 1.1.0
    77 * Author: OmadaAI
    88 * License: GPLv2 or later
    99 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1010 * Text Domain: omadaai
     11 * Domain Path: /languages
    1112 */
     13
    1214if ( ! defined( 'ABSPATH' ) ) {
    1315    exit;
     
    1719 * Plugin constants
    1820 */
    19 define( 'OMADAAI_VERSION', '1.0.0' );
     21define( 'OMADAAI_VERSION', '1.1.0' );
    2022define( 'OMADAAI_PATH', plugin_dir_path( __FILE__ ) );
    2123define( 'OMADAAI_URL', plugin_dir_url( __FILE__ ) );
    22 
    2324
    2425/**
     
    3839 * Bootstrap plugin
    3940 */
     41
    4042add_action(
    4143    'plugins_loaded',
  • omadaai/trunk/readme.txt

    r3439711 r3441122  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 1.0.0
     7Stable tag: 1.1.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1313== Description ==
    1414
    15 Omada AI helps website owners install and manage the Omada Chat widget seamlessly on their WordPress sites.
     15OmadaAI helps website owners install and manage the Omada Chat widget seamlessly on their WordPress sites.
    1616
    1717The plugin provides a simple admin settings page where site administrators can configure their Omada Chat credentials (Agent ID, Workspace ID, and Access Token). Once enabled, the chat widget is automatically injected on all public-facing pages of the website.
     
    5454== Screenshots ==
    5555
    56 1. Omada AI admin settings page
     561. OmadaAI admin settings page
    57572. Chat widget displayed on the front-end website
    5858
    5959== Changelog ==
     60
     61= 1.1.0 =
     62* Reliability: Fixed REST route registration timing to ensure endpoints always load
     63* Security: Standardized text domains, corrected HTTP status codes (401/403)
     64* Security/PHPCS: Sanitized/unslashed superglobals in auth handler
     65* Performance: Set options autoload=no; optimized admin asset handling
     66* UX: Added password reveal toggle in settings
     67* API: Added argument validation schemas for REST endpoints
    6068
    6169= 1.0.0 =
     
    6775== Upgrade Notice ==
    6876
     77= 1.1.0 =
     78This update improves REST reliability, security, and admin UX. Update recommended.
     79
    6980= 1.0.0 =
    7081Initial release of Omada AI.
Note: See TracChangeset for help on using the changeset viewer.