Changeset 3441122
- Timestamp:
- 01/16/2026 03:52:10 PM (3 months ago)
- Location:
- omadaai/trunk
- Files:
-
- 7 added
- 7 edited
-
admin/js/admin-scripts.js (modified) (1 diff)
-
includes/api (added)
-
includes/api/class-content-automation-api.php (added)
-
includes/api/class-widget-config-api.php (added)
-
includes/class-admin.php (modified) (4 diffs)
-
includes/class-chat-widget.php (modified) (8 diffs)
-
includes/class-loader.php (modified) (5 diffs)
-
includes/class-omada-basic-auth.php (added)
-
includes/class-rest.php (modified) (2 diffs)
-
languages (added)
-
languages/index.php (added)
-
omadaai.php (modified) (3 diffs)
-
readme.txt (modified) (4 diffs)
-
uninstall.php (added)
Legend:
- Unmodified
- Added
- Removed
-
omadaai/trunk/admin/js/admin-scripts.js
r3439716 r3441122 2 2 $('.toggle-password').on('click', function () { 3 3 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'); 5 7 }); 6 8 }); -
omadaai/trunk/includes/class-admin.php
r3439716 r3441122 119 119 120 120 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 ]; 131 130 } 132 131 … … 146 145 $s = wp_parse_args( get_option( 'omada_chat_settings', [] ), $defaults ); 147 146 ?> 148 149 147 <div class="wrap omada-admin"> 150 148 <h1><?php esc_html_e( 'Omada Chat – Settings', 'omadaai' ); ?></h1> … … 153 151 154 152 <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' ); ?> 159 154 160 155 <h2 class="section-title"><?php esc_html_e( 'Chat Widget Configuration', 'omadaai' ); ?></h2> … … 170 165 <div class="field"> 171 166 <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> 173 168 </div> 174 169 175 170 <div class="field"> 176 171 <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> 178 173 </div> 179 174 180 175 <div class="field"> 181 176 <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> 183 181 </div> 184 182 -
omadaai/trunk/includes/class-chat-widget.php
r3439716 r3441122 7 7 * Omada Chat Widget Loader (Frontend only) 8 8 * 9 * ✔ Safe for WordPress.org10 * ✔ No global CSS pollution11 * ✔ No JS DOM hacks12 * ✔ Anchor (<a>) works correctly9 * ✔ WordPress.org safe 10 * ✔ Anchor issue RESOLVED (scoped) 11 * ✔ No DOM mutation 12 * ✔ No global CSS hacks 13 13 */ 14 14 class Omada_Chat_Widget { … … 18 18 } 19 19 20 /**21 * Enqueue widget assets safely22 */23 20 public function enqueue_assets() { 24 21 25 // ❌ Never run in admin26 22 if ( is_admin() ) { 27 23 return; … … 30 26 $settings = get_option( 'omada_chat_settings', [] ); 31 27 32 /**33 * 🔴 HARD DISABLE34 * If disabled → do NOTHING (no script, no CSS)35 */36 if ( empty( $settings['enabled'] ) ) {37 return;38 }39 40 /**41 * 🟡 Validate required config42 */43 28 if ( 29 empty( $settings['enabled'] ) || 44 30 empty( $settings['agent_id'] ) || 45 31 empty( $settings['workspace_id'] ) || … … 50 36 } 51 37 52 /** 53 * 1️⃣ Inject config BEFORE widget CDN 54 * (INLINE ONLY – WordPress.org safe) 55 */ 38 /* 1️⃣ Inject config */ 56 39 wp_register_script( 57 40 'omada-chat-config', … … 61 44 true 62 45 ); 63 64 46 wp_enqueue_script( 'omada-chat-config' ); 65 47 66 48 wp_add_inline_script( 67 49 '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 ] ) . ';', 75 55 'before' 76 56 ); 77 57 78 /** 79 * 2️⃣ Load external Omada widget CDN 80 */ 58 /* 2️⃣ Load widget */ 81 59 wp_enqueue_script( 82 60 'omada-chat-widget', … … 87 65 ); 88 66 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 */ 94 77 wp_register_style( 95 78 'omada-chat-style', … … 98 81 OMADAAI_VERSION 99 82 ); 100 101 83 wp_enqueue_style( 'omada-chat-style' ); 102 84 … … 104 86 'omada-chat-style', 105 87 ' 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; 115 97 } 116 98 ' -
omadaai/trunk/includes/class-loader.php
r3439716 r3441122 21 21 self::load_dependencies(); 22 22 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. 23 25 self::init_rest(); 24 26 } … … 29 31 private static function load_dependencies() { 30 32 33 // Core 31 34 require_once OMADAAI_PATH . 'includes/class-chat-widget.php'; 32 35 require_once OMADAAI_PATH . 'includes/class-admin.php'; 33 36 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'; 34 44 } 35 45 … … 39 49 private static function init_components() { 40 50 41 // Admin UI42 51 if ( is_admin() && class_exists( 'Omada_Chat_Admin' ) ) { 43 52 new Omada_Chat_Admin(); 44 53 } 45 54 46 // Frontend widget47 55 if ( ! is_admin() && class_exists( 'Omada_Chat_Widget' ) ) { 48 56 new Omada_Chat_Widget(); … … 53 61 * Initialize REST functionality 54 62 * 55 * REST routes should only be registeredon rest_api_init63 * REST routes MUST be registered only on rest_api_init 56 64 */ 57 65 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) 59 75 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(); 66 77 } 67 78 } … … 82 93 'script_url' => 'https://chatwidget.omada-prod.io/v1.0/prod-omadacdn.js', 83 94 'updated_at' => current_time( 'mysql' ), 84 ] 95 ], 96 '', 97 false 85 98 ); 86 99 } -
omadaai/trunk/includes/class-rest.php
r3439716 r3441122 5 5 6 6 /** 7 * Legacy REST placeholder class.7 * Omada_Chat_REST 8 8 * 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. 11 19 * 12 20 * @since 1.0.0 … … 15 23 16 24 /** 17 * Constructor .25 * Constructor 18 26 * 19 * Intentionally left empty. 20 * No hooks, no routes, no side effects. 27 * Do NOT hook rest_api_init here 21 28 */ 22 29 public function __construct() { 23 // Do nothing.30 // Intentionally empty. 24 31 } 25 32 -
omadaai/trunk/omadaai.php
r3439716 r3441122 4 4 * Plugin URI: https://omada.ai 5 5 * Description: Install Omada Chat widget on your website with one click. 6 * Version: 1. 0.06 * Version: 1.1.0 7 7 * Author: OmadaAI 8 8 * License: GPLv2 or later 9 9 * License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 * Text Domain: omadaai 11 * Domain Path: /languages 11 12 */ 13 12 14 if ( ! defined( 'ABSPATH' ) ) { 13 15 exit; … … 17 19 * Plugin constants 18 20 */ 19 define( 'OMADAAI_VERSION', '1. 0.0' );21 define( 'OMADAAI_VERSION', '1.1.0' ); 20 22 define( 'OMADAAI_PATH', plugin_dir_path( __FILE__ ) ); 21 23 define( 'OMADAAI_URL', plugin_dir_url( __FILE__ ) ); 22 23 24 24 25 /** … … 38 39 * Bootstrap plugin 39 40 */ 41 40 42 add_action( 41 43 'plugins_loaded', -
omadaai/trunk/readme.txt
r3439711 r3441122 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1. 0.07 Stable tag: 1.1.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 13 13 == Description == 14 14 15 Omada AI helps website owners install and manage the Omada Chat widget seamlessly on their WordPress sites.15 OmadaAI helps website owners install and manage the Omada Chat widget seamlessly on their WordPress sites. 16 16 17 17 The 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. … … 54 54 == Screenshots == 55 55 56 1. Omada AI admin settings page56 1. OmadaAI admin settings page 57 57 2. Chat widget displayed on the front-end website 58 58 59 59 == 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 60 68 61 69 = 1.0.0 = … … 67 75 == Upgrade Notice == 68 76 77 = 1.1.0 = 78 This update improves REST reliability, security, and admin UX. Update recommended. 79 69 80 = 1.0.0 = 70 81 Initial release of Omada AI.
Note: See TracChangeset
for help on using the changeset viewer.