class-wawp-enqueue-scripts.php
910 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Enqueue scripts and styles. |
| 4 | * |
| 5 | * @package automation-web-platform |
| 6 | */ |
| 7 | |
| 8 | if ( ! defined( 'ABSPATH' ) ) { |
| 9 | exit; |
| 10 | } |
| 11 | |
| 12 | |
| 13 | /** |
| 14 | * Class WAWP_Enqueue_Scripts |
| 15 | * Handles enqueuing of scripts and styles. |
| 16 | */ |
| 17 | class WAWP_Enqueue_Scripts { |
| 18 | |
| 19 | /** |
| 20 | * Enqueue admin styles and scripts. |
| 21 | */ |
| 22 | public static function enqueue_admin_styles_scripts() { |
| 23 | $screen = get_current_screen(); |
| 24 | if ( ! $screen ) { |
| 25 | return; |
| 26 | } |
| 27 | |
| 28 | $is_wawp_page = strpos( $screen->id, 'wawp' ) !== false; |
| 29 | |
| 30 | $section_raw = filter_input( INPUT_GET, 'wawp_section', FILTER_SANITIZE_SPECIAL_CHARS ); |
| 31 | $section = $section_raw ? sanitize_key( $section_raw ) : 'dashboard'; |
| 32 | |
| 33 | if ( $is_wawp_page ) { |
| 34 | if ( in_array( $section, array( 'dashboard', 'senders', 'activity_hub', 'system_info', 'notifications', 'email_templates', 'chat_widget', 'country-code', 'google_recaptcha', 'authentication-pages', 'otp_messages', 'registration-form', 'passwordless-login', 'whatsapp-web-sender', 'smtp-sender', 'block-manager' ), true ) ) { |
| 35 | // List core WP dependencies. |
| 36 | $dependencies = array( 'jquery', 'wp-polyfill', 'wp-element', 'wp-i18n', 'wp-api-fetch', 'wp-hooks', 'wp-data', 'wp-util', 'wp-api', 'wp-components', 'wp-url', 'underscore', 'backbone' ); |
| 37 | |
| 38 | // Dequeue WP Auth Check to prevent "hasClass of undefined" errors in SPA context. |
| 39 | wp_dequeue_script( 'wp-auth-check' ); |
| 40 | wp_dequeue_script( 'heartbeat' ); |
| 41 | |
| 42 | wp_enqueue_style( 'wawp-local-fonts', WAWP_PLUGIN_URL . 'assets/css/wawp-fonts.css', array(), '1.0.0' ); |
| 43 | |
| 44 | // Load React Application. |
| 45 | // Find the hashed index file (e.g. index-AbCdEf.js) generated by Vite. |
| 46 | $dist_assets_dir = WAWP_PLUGIN_DIR . 'app/dist/assets/'; |
| 47 | $index_js_file = self::find_hashed_asset( $dist_assets_dir, 'index', 'js' ); |
| 48 | $index_css_file = self::find_hashed_asset( $dist_assets_dir, 'index', 'css' ); |
| 49 | |
| 50 | $index_js_path = $dist_assets_dir . $index_js_file; |
| 51 | $index_css_path = $dist_assets_dir . $index_css_file; |
| 52 | |
| 53 | // Use filemtime as version — the hashed filename already ensures cache busting, |
| 54 | // so we don't need wawp_force_cache_bust_version for the entry file anymore. |
| 55 | $js_ver = file_exists( $index_js_path ) ? filemtime( $index_js_path ) : WAWP_PLUGIN_VERSION; |
| 56 | $css_ver = file_exists( $index_css_path ) ? filemtime( $index_css_path ) : WAWP_PLUGIN_VERSION; |
| 57 | |
| 58 | wp_enqueue_media(); |
| 59 | wp_enqueue_script( 'wawp-react-dashboard', WAWP_PLUGIN_URL . 'app/dist/assets/' . $index_js_file, $dependencies, $js_ver, true ); |
| 60 | wp_enqueue_style( 'wawp-react-dashboard-css', WAWP_PLUGIN_URL . 'app/dist/assets/' . $index_css_file, array(), $css_ver ); |
| 61 | wp_enqueue_style( 'wawp-remix-icon', WAWP_PLUGIN_URL . 'assets/css/resources/remixicon.css', array(), '4.6.0' ); |
| 62 | wp_enqueue_style( 'wawp-chat-widget-style', WAWP_PLUGIN_URL . 'assets/css/style.css', array(), WAWP_PLUGIN_VERSION ); |
| 63 | |
| 64 | add_filter( |
| 65 | 'script_loader_tag', |
| 66 | function ( $tag, $handle ) { |
| 67 | if ( 'wawp-react-dashboard' === $handle ) { |
| 68 | return str_replace( '<script ', '<script type="module" ', $tag ); |
| 69 | } |
| 70 | return $tag; |
| 71 | }, |
| 72 | 10, |
| 73 | 2 |
| 74 | ); |
| 75 | |
| 76 | if ( function_exists( 'wp_set_script_translations' ) ) { |
| 77 | wp_set_script_translations( 'wawp-react-dashboard', 'automation-web-platform', WAWP_PLUGIN_DIR . 'languages' ); |
| 78 | } |
| 79 | |
| 80 | self::localize_react_dashboard_data( $section ); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | // 4. Custom Messages (Users/Profile) |
| 85 | if ( in_array( $screen->base, array( 'users', 'profile', 'user-edit' ), true ) ) { |
| 86 | wp_enqueue_style( 'wawp-badges', WAWP_PLUGIN_URL . 'assets/css/wawp-badges.css', array(), WAWP_PLUGIN_VERSION ); |
| 87 | |
| 88 | if ( wawp()->senders->is_whatsapp_active() ) { |
| 89 | wp_enqueue_script( 'wawp-send-msg', WAWP_PLUGIN_URL . 'assets/js/wawp-send-msg.js', array( 'jquery' ), WAWP_PLUGIN_VERSION, true ); |
| 90 | |
| 91 | $online_instances = WAWP()->instance_manager->get_online_instances_cached(); |
| 92 | $online = array(); |
| 93 | if ( $online_instances ) { |
| 94 | foreach ( $online_instances as $inst ) { |
| 95 | $online[] = array( |
| 96 | 'instance_id' => $inst->instance_id, |
| 97 | 'name' => $inst->name, |
| 98 | ); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | wp_localize_script( |
| 103 | 'wawp-send-msg', |
| 104 | 'wawpSendMsgData', |
| 105 | array( |
| 106 | 'ajaxUrl' => admin_url( 'admin-ajax.php' ), |
| 107 | 'security' => wp_create_nonce( 'wawp_send_msg_nonce' ), |
| 108 | 'onlineInstances' => $online, |
| 109 | 'noOnlineInstance' => esc_html__( 'No online instances found.', 'automation-web-platform' ), |
| 110 | ) |
| 111 | ); |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | |
| 117 | |
| 118 | /** |
| 119 | * Enqueue frontend styles and scripts. |
| 120 | */ |
| 121 | public static function enqueue_frontend_styles_scripts() { |
| 122 | $is_custom_pages_enabled = WAWP()->get_wawp_setting( WAWP_Settings_Registry::CUSTOM_PAGES_ENABLED, 1 ); |
| 123 | $is_replace_wc_forms = WAWP()->get_wawp_setting( WAWP_Settings_Registry::REPLACE_WC_FORMS ); |
| 124 | $is_replace_wc_checkout_login = WAWP()->get_wawp_setting( WAWP_Settings_Registry::REPLACE_WC_CHECKOUT_LOGIN ); |
| 125 | |
| 126 | if ( $is_custom_pages_enabled && ! is_admin() ) { |
| 127 | $enqueue_overrides = false; |
| 128 | |
| 129 | if ( $is_replace_wc_forms && function_exists( 'is_account_page' ) && is_account_page() ) { |
| 130 | $enqueue_overrides = true; |
| 131 | } |
| 132 | |
| 133 | if ( $is_replace_wc_checkout_login && function_exists( 'is_checkout' ) && is_checkout() ) { |
| 134 | $enqueue_overrides = true; |
| 135 | } |
| 136 | |
| 137 | if ( $enqueue_overrides ) { |
| 138 | wp_enqueue_style( 'wawp-frontend-overrides', WAWP_PLUGIN_URL . 'assets/css/wawp-frontend-overrides.css', array(), WAWP_PLUGIN_VERSION ); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | // Central Data Hub for all frontend localized variables. |
| 143 | $global_vars = apply_filters( 'wawp_frontend_global_vars', array() ); |
| 144 | if ( ! empty( $global_vars ) ) { |
| 145 | $inline_script = ''; |
| 146 | foreach ( $global_vars as $var_name => $data ) { |
| 147 | $inline_script .= 'var ' . esc_js( $var_name ) . ' = ' . wp_json_encode( $data ) . ";\n"; |
| 148 | } |
| 149 | wp_register_script( 'wawp-global-data-hub', false, array(), WAWP_PLUGIN_VERSION, true ); |
| 150 | wp_enqueue_script( 'wawp-global-data-hub' ); |
| 151 | wp_add_inline_script( 'wawp-global-data-hub', $inline_script ); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | /** |
| 156 | * Localize React dashboard data. |
| 157 | * |
| 158 | * @param string $section Current section. |
| 159 | */ |
| 160 | public static function localize_react_dashboard_data( $section ) { |
| 161 | $user_id = get_current_user_id(); |
| 162 | $locale = get_user_locale(); |
| 163 | $cache_key = 'wawp_dashboard_data_full_' . $user_id . '_' . $locale; |
| 164 | |
| 165 | if ( filter_input( INPUT_GET, 'force_refresh' ) ) { |
| 166 | delete_transient( $cache_key ); |
| 167 | wp_cache_delete( $cache_key, 'wawp_database' ); |
| 168 | delete_transient( 'wawp_latest_plugin_version' ); |
| 169 | } |
| 170 | |
| 171 | $data = wp_cache_get( $cache_key, 'wawp_database' ); |
| 172 | |
| 173 | if ( false === $data ) { |
| 174 | $data = get_transient( $cache_key ); |
| 175 | if ( false === $data ) { |
| 176 | $data = self::prepare_dashboard_data( $section, true ); |
| 177 | set_transient( $cache_key, $data, 60 ); // Short transient for live data. |
| 178 | |
| 179 | // Track keys for mass clearing without direct SQL. |
| 180 | $keys = get_option( 'wawp_dashboard_transient_keys', array() ); |
| 181 | if ( ! is_array( $keys ) ) { |
| 182 | $keys = array(); |
| 183 | } |
| 184 | if ( ! in_array( $cache_key, $keys, true ) ) { |
| 185 | $keys[] = $cache_key; |
| 186 | update_option( 'wawp_dashboard_transient_keys', $keys, false ); |
| 187 | } |
| 188 | } |
| 189 | wp_cache_set( $cache_key, $data, 'wawp_database', 60 ); |
| 190 | } |
| 191 | |
| 192 | // Dynamically override the active section for the SPA to know where we landed. |
| 193 | // Also override popupStep from the live URL — cache always stores 0, so we must re-read it here. |
| 194 | if ( isset( $data['global'] ) ) { |
| 195 | $data['global']['section'] = $section; |
| 196 | $data['global']['popupStep'] = (int) filter_input( INPUT_GET, 'wawp_popup_step', FILTER_SANITIZE_NUMBER_INT ); |
| 197 | } |
| 198 | |
| 199 | wp_localize_script( 'wawp-react-dashboard', 'wawpDashboardData', $data ); |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Prepare data for the dashboard. |
| 204 | * |
| 205 | * @param string $section Current section. |
| 206 | * @param bool $is_full Whether to return full data. |
| 207 | * @return array Dashboard data. |
| 208 | */ |
| 209 | public static function prepare_dashboard_data( $section, $is_full = false ) { |
| 210 | try { |
| 211 | // Dynamic Translation Reloader for user locale (to support REST API requests loaded under user locale). |
| 212 | if ( function_exists( 'get_user_locale' ) ) { |
| 213 | $user_locale = get_user_locale(); |
| 214 | if ( $user_locale && get_locale() !== $user_locale ) { |
| 215 | $mofile = WAWP_PLUGIN_DIR . 'languages/automation-web-platform-' . $user_locale . '.mo'; |
| 216 | if ( file_exists( $mofile ) ) { |
| 217 | load_textdomain( 'automation-web-platform', $mofile ); |
| 218 | } else { |
| 219 | $parts = explode( '_', $user_locale ); |
| 220 | if ( count( $parts ) > 1 ) { |
| 221 | $fallback_mofile = WAWP_PLUGIN_DIR . 'languages/automation-web-platform-' . $parts[0] . '.mo'; |
| 222 | if ( file_exists( $fallback_mofile ) ) { |
| 223 | load_textdomain( 'automation-web-platform', $fallback_mofile ); |
| 224 | } |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | if ( ! function_exists( 'is_plugin_active' ) ) { |
| 231 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 232 | } |
| 233 | |
| 234 | $user_data = array( |
| 235 | 'user_email' => wp_get_current_user()->user_email, |
| 236 | 'plan_name' => 'Pro Lifetime', |
| 237 | 'is_lifetime' => true, |
| 238 | 'avatar' => '', |
| 239 | ); |
| 240 | $connector = wawp()->senders; |
| 241 | $is_connected = true; |
| 242 | $is_sso = true; |
| 243 | $icons_url = WAWP_PLUGIN_URL . 'assets/icons/'; |
| 244 | |
| 245 | // Sidebar logic. |
| 246 | $issues_count = 0; |
| 247 | if ( WAWP()->system_info ) { |
| 248 | $issues_count = WAWP()->system_info->get_cached_issue_count(); |
| 249 | } |
| 250 | |
| 251 | // Notices. |
| 252 | $notices = array(); |
| 253 | if ( class_exists( 'WAWP_Admin_Notices' ) ) { |
| 254 | if ( method_exists( 'WAWP_Admin_Notices', 'is_wa_offline' ) && WAWP_Admin_Notices::is_wa_offline() ) { |
| 255 | $notices[] = array( |
| 256 | 'type' => 'offline', |
| 257 | 'url' => admin_url( 'admin.php?page=wawp&wawp_section=whatsapp-web-sender' ), |
| 258 | ); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | $country_options = WAWP()->get_wawp_setting( 'woo_intl_tel_options', array() ); |
| 263 | $default_country = ! empty( $country_options['default_country_code'] ) ? strtoupper( $country_options['default_country_code'] ) : 'US'; |
| 264 | |
| 265 | // Detect if any caching plugin is active. |
| 266 | $cache_plugins = array( |
| 267 | 'wp-rocket/wp-rocket.php' => 'WP Rocket', |
| 268 | 'litespeed-cache/litespeed-cache.php' => 'LiteSpeed Cache', |
| 269 | 'w3-total-cache/w3-total-cache.php' => 'W3 Total Cache', |
| 270 | 'wp-super-cache/wp-cache.php' => 'WP Super Cache', |
| 271 | 'wp-fastest-cache/wpFastestCache.php' => 'WP Fastest Cache', |
| 272 | 'sg-cachepress/sg-cachepress.php' => 'SG Optimizer', |
| 273 | 'breeze/breeze.php' => 'Breeze', |
| 274 | 'autoptimize/autoptimize.php' => 'Autoptimize', |
| 275 | 'cache-enabler/cache-enabler.php' => 'Cache Enabler', |
| 276 | 'wp-hummingbird/wp-hummingbird.php' => 'Hummingbird', |
| 277 | 'hummingbird-performance/wp-hummingbird.php' => 'Hummingbird', |
| 278 | 'swift-performance-lite/swift-performance-lite.php' => 'Swift Performance', |
| 279 | 'flying-press/flying-press.php' => 'FlyingPress', |
| 280 | 'nitropack/nitropack.php' => 'NitroPack', |
| 281 | ); |
| 282 | $active_cache_plugins = array(); |
| 283 | foreach ( $cache_plugins as $plugin_path => $plugin_name ) { |
| 284 | if ( is_plugin_active( $plugin_path ) ) { |
| 285 | $active_cache_plugins[] = $plugin_name; |
| 286 | } |
| 287 | } |
| 288 | $has_active_cache = ! empty( $active_cache_plugins ); |
| 289 | |
| 290 | // 1. Base Global Data (Always present) |
| 291 | $data = array( |
| 292 | 'notices' => $notices, |
| 293 | 'global' => array( |
| 294 | 'wpRestNonce' => wp_create_nonce( 'wp_rest' ), |
| 295 | 'apiRestUrl' => get_rest_url( null, 'wawp/v1' ), |
| 296 | 'restUrl' => get_rest_url( null, 'wawp/v1' ), |
| 297 | 'settingsRestUrl' => get_rest_url( null, 'wawp/v1/settings' ), |
| 298 | 'ajaxUrl' => admin_url( 'admin-ajax.php' ), |
| 299 | 'nonce' => wp_create_nonce( 'wawp_nonce' ), |
| 300 | 'isRtl' => is_rtl() || ( function_exists( 'get_user_locale' ) && 'ar' === substr( get_user_locale(), 0, 2 ) ), |
| 301 | 'version' => WAWP_PLUGIN_VERSION, |
| 302 | 'pluginUrl' => WAWP_PLUGIN_URL, |
| 303 | 'popupStep' => (int) filter_input( INPUT_GET, 'wawp_popup_step', FILTER_SANITIZE_NUMBER_INT ), |
| 304 | 'section' => $section, |
| 305 | 'country' => $default_country, |
| 306 | 'adminEmail' => wp_get_current_user()->user_email, |
| 307 | 'currentLocale' => get_locale(), |
| 308 | 'currentUserLocale' => get_user_locale(), |
| 309 | 'availableLanguages' => self::get_site_available_languages(), |
| 310 | 'languageSwitchNonce' => wp_create_nonce( 'wawp_switch_language' ), |
| 311 | 'flushCacheNonce' => wp_create_nonce( 'wawp_flush_cache' ), |
| 312 | 'hasActiveCache' => $has_active_cache, |
| 313 | 'activeCachePlugins' => $active_cache_plugins, |
| 314 | ), |
| 315 | 'nonce' => wp_create_nonce( 'wawp_nonce' ), |
| 316 | 'ajaxUrl' => admin_url( 'admin-ajax.php' ), |
| 317 | 'rtl' => is_rtl() || ( function_exists( 'get_user_locale' ) && 'ar' === substr( get_user_locale(), 0, 2 ) ), |
| 318 | 'i18n' => array(), |
| 319 | ); |
| 320 | |
| 321 | // 2. Full Sidebar and Stats (If full load or dashboard) |
| 322 | if ( $is_full || 'dashboard' === $section ) { |
| 323 | $dashboard = WAWP_Dashboard::get_instance(); |
| 324 | $dashboard_data = $dashboard->get_dashboard_data(); |
| 325 | $data['sidebarData'] = array( |
| 326 | 'issuesCount' => $issues_count, |
| 327 | 'enabledSections' => $dashboard_data['features'] ?? array(), |
| 328 | 'enabledSenders' => array( |
| 329 | 'email' => $connector->is_email_sender_enabled(), |
| 330 | 'wa' => $connector->is_whatsapp_sender_enabled(), |
| 331 | 'block' => $connector->is_block_manager_active(), |
| 332 | ), |
| 333 | 'allowedSenders' => array( |
| 334 | 'email' => true, |
| 335 | 'wa' => true, |
| 336 | 'block' => true, |
| 337 | ), |
| 338 | ); |
| 339 | $data['i18n'] = $dashboard_data['i18n'] ?? array(); |
| 340 | $data['urls'] = $dashboard_data['urls'] ?? array(); |
| 341 | $data['features'] = $dashboard_data['features'] ?? array(); |
| 342 | $data['isGuest'] = $dashboard_data['isGuest'] ?? false; |
| 343 | $data['stats'] = $dashboard_data['stats'] ?? array(); |
| 344 | $data['wooStatus'] = $dashboard_data['wooStatus'] ?? array(); |
| 345 | $data['icons'] = array( |
| 346 | 'baseUrl' => $icons_url, |
| 347 | 'successGif' => WAWP_PLUGIN_URL . 'assets/img/success.gif', |
| 348 | 'errorGif' => WAWP_PLUGIN_URL . 'assets/img/error.gif', |
| 349 | ); |
| 350 | } |
| 351 | |
| 352 | // 3. Section Specific Data (Always include if mapped) |
| 353 | $mapped_section = $section; |
| 354 | if ( 'activity_hub' === $section ) { |
| 355 | $mapped_section = 'activity-hub'; |
| 356 | } elseif ( 'email_templates' === $section ) { |
| 357 | $mapped_section = 'email-templates'; |
| 358 | } elseif ( 'otp_messages' === $section ) { |
| 359 | $mapped_section = 'passwordless-login'; |
| 360 | } |
| 361 | |
| 362 | // 3. Section Specific Data Dispatch (Registry Pattern - Point 4) |
| 363 | $handlers = array( |
| 364 | 'senders' => array( 'WAWP_Senders', 'get_instance' ), |
| 365 | 'whatsapp-web-sender' => array( 'WAWP_Senders', 'get_instance' ), |
| 366 | 'registration-form' => array( 'WAWP_Signup', 'get_instance' ), |
| 367 | 'passwordless-login' => array( 'WAWP_Otp_Login', 'get_instance' ), |
| 368 | 'system_info' => array( 'WAWP_System_Info', 'get_instance' ), |
| 369 | 'notifications' => array( 'WAWP_Flow_Builder', 'get_instance' ), |
| 370 | 'activity-hub' => array( 'WAWP_Unified_Log', 'get_instance' ), |
| 371 | 'google_recaptcha' => array( 'WAWP_Google_Recaptcha', 'get_instance' ), |
| 372 | 'chat_widget' => array( 'WAWP_Chat_Widget', 'get_instance' ), |
| 373 | 'smtp-sender' => array( 'WAWP_Email_Sender', 'get_instance' ), |
| 374 | 'block-manager' => array( 'WAWP_Block_Manager', 'get_instance' ), |
| 375 | 'email-templates' => array( 'WAWP_Email_Templates', 'get_instance' ), |
| 376 | 'authentication-pages' => array( 'WAWP_Custom_Pages_Settings', 'get_instance' ), |
| 377 | 'country-code' => array( 'WAWP_CountryCode', 'get_instance' ), |
| 378 | ); |
| 379 | |
| 380 | // Key Mapping for the localized object. |
| 381 | $key_map = array( |
| 382 | 'senders' => 'sendersData', |
| 383 | 'whatsapp-web-sender' => 'instancesData', |
| 384 | 'registration-form' => 'registrationForm', |
| 385 | 'passwordless-login' => 'passwordlessLoginSettings', |
| 386 | 'system_info' => 'systemInfo', |
| 387 | 'notifications' => 'notificationsData', |
| 388 | 'activity-hub' => 'logsData', |
| 389 | 'google_recaptcha' => 'recaptchaData', |
| 390 | 'chat_widget' => 'chatWidgetData', |
| 391 | 'smtp-sender' => 'smtpSenderData', |
| 392 | 'block-manager' => 'blockManagerData', |
| 393 | 'email-templates' => 'emailTemplatesData', |
| 394 | 'authentication-pages' => 'authPages', |
| 395 | 'country-code' => 'phoneFieldData', |
| 396 | ); |
| 397 | |
| 398 | $sections_to_load = $is_full ? array_keys( $handlers ) : array( $mapped_section ); |
| 399 | |
| 400 | foreach ( $sections_to_load as $current_section ) { |
| 401 | if ( ! isset( $handlers[ $current_section ] ) ) { |
| 402 | continue; |
| 403 | } |
| 404 | |
| 405 | $class = $handlers[ $current_section ][0]; |
| 406 | $instance = null; |
| 407 | |
| 408 | // Point 1: Get existing singleton instance instead of 'new Class()'. |
| 409 | switch ( $class ) { |
| 410 | case 'WAWP_Senders': |
| 411 | $instance = WAWP()->senders; |
| 412 | break; |
| 413 | case 'WAWP_System_Info': |
| 414 | $instance = WAWP()->system_info; |
| 415 | break; |
| 416 | case 'WAWP_Flow_Builder': |
| 417 | $instance = WAWP()->flow_builder; |
| 418 | break; |
| 419 | case 'WAWP_Unified_Log': |
| 420 | $instance = WAWP()->unified_log; |
| 421 | break; |
| 422 | case 'WAWP_Chat_Widget': |
| 423 | $instance = WAWP()->chat_widget; |
| 424 | break; |
| 425 | case 'WAWP_Block_Manager': |
| 426 | $instance = WAWP()->block_manager; |
| 427 | break; |
| 428 | default: |
| 429 | if ( method_exists( $class, 'get_instance' ) ) { |
| 430 | $instance = call_user_func( array( $class, 'get_instance' ) ); |
| 431 | } |
| 432 | break; |
| 433 | } |
| 434 | |
| 435 | if ( $instance ) { |
| 436 | $method = 'get_react_settings_data'; |
| 437 | // Handle special method names for legacy support. |
| 438 | if ( 'whatsapp-web-sender' === $current_section ) { |
| 439 | $method = 'get_whatsapp_react_settings_data'; |
| 440 | } elseif ( 'system_info' === $current_section ) { |
| 441 | $method = 'get_react_system_info_data'; |
| 442 | } elseif ( 'block-manager' === $current_section ) { |
| 443 | $method = 'get_data'; |
| 444 | } |
| 445 | |
| 446 | if ( method_exists( $instance, $method ) ) { |
| 447 | $key = $key_map[ $current_section ] ?? $current_section; |
| 448 | $section_data = $instance->$method(); |
| 449 | $data[ $key ] = $section_data; |
| 450 | |
| 451 | // Merge translations if provided. |
| 452 | // During a full load, section-specific pageTitle/pageSubtitle must NOT |
| 453 | // overwrite the dashboard's own pageTitle/pageSubtitle that was already set. |
| 454 | if ( isset( $section_data['i18n'] ) ) { |
| 455 | $preserved_page_title = $data['i18n']['pageTitle'] ?? ''; |
| 456 | $preserved_page_subtitle = $data['i18n']['pageSubtitle'] ?? ''; |
| 457 | $data['i18n'] = array_merge( $data['i18n'], $section_data['i18n'] ); |
| 458 | // Restore the dashboard-level page title/subtitle so no section overwrites them. |
| 459 | if ( '' !== $preserved_page_title ) { |
| 460 | $data['i18n']['pageTitle'] = $preserved_page_title; |
| 461 | } |
| 462 | if ( '' !== $preserved_page_subtitle ) { |
| 463 | $data['i18n']['pageSubtitle'] = $preserved_page_subtitle; |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | // Merge status messages if provided. |
| 468 | if ( isset( $section_data['status_messages'] ) ) { |
| 469 | $data['status_messages'] = array_merge( $data['status_messages'] ?? array(), $section_data['status_messages'] ); |
| 470 | } |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | // Special additions for specific sections. |
| 475 | if ( 'whatsapp-web-sender' === $current_section ) { |
| 476 | // OTP Settings & Status. |
| 477 | $otp_settings = WAWP()->from_db( WAWP()->get_wawp_setting( WAWP_Settings_Registry::OTP_SETTINGS, array() ) ); |
| 478 | $otp_settings['enabled'] = (int) WAWP()->get_wawp_setting( WAWP_Settings_Registry::OTP_ENABLED, 0 ); |
| 479 | $otp_settings['login_enabled'] = (int) WAWP()->get_wawp_setting( WAWP_Settings_Registry::PASSWORDLESS_LOGIN_ENABLED, 0 ); |
| 480 | $otp_settings['signup_enabled'] = (int) WAWP()->get_wawp_setting( WAWP_Settings_Registry::SIGNUP_ENABLED, 0 ); |
| 481 | $data['otp'] = $otp_settings; |
| 482 | |
| 483 | // Signup Settings. |
| 484 | $signup_settings = WAWP()->from_db( WAWP()->get_wawp_setting( WAWP_Settings_Registry::REGISTRATION_FORM_SETTINGS, array() ) ); |
| 485 | $signup_settings['enabled'] = (int) WAWP()->get_wawp_setting( WAWP_Settings_Registry::SIGNUP_ENABLED, 0 ); |
| 486 | $data['signup'] = $signup_settings; |
| 487 | |
| 488 | // Notifications Settings. |
| 489 | $notif_settings = WAWP()->from_db( WAWP()->get_wawp_setting( WAWP_Settings_Registry::NOTIFICATIONS_RULES, array() ) ); |
| 490 | $notif_settings['enabled'] = (int) WAWP()->get_wawp_setting( WAWP_Settings_Registry::NOTIFICATIONS_ENABLED, 0 ); |
| 491 | $notif_settings['selected_instance_ids'] = WAWP()->get_wawp_setting( WAWP_Settings_Registry::NOTIF_SELECTED_INSTANCE_IDS, '' ); |
| 492 | $notif_settings['admin_selected_instance_ids'] = WAWP()->get_wawp_setting( WAWP_Settings_Registry::NOTIF_ADMIN_SELECTED_INSTANCE_IDS, '' ); |
| 493 | $data['notif'] = $notif_settings; |
| 494 | |
| 495 | $data['general_instance'] = WAWP()->get_wawp_setting( WAWP_Settings_Registry::GENERAL_INSTANCE, '' ); |
| 496 | } |
| 497 | |
| 498 | // Activity Hub: inject Meta templates, placeholders, instances & sender status for the Send Message drawer. |
| 499 | if ( 'activity-hub' === $current_section ) { |
| 500 | $data['metaTemplates'] = array(); |
| 501 | $data['placeholders'] = ( class_exists( 'WAWP_Flow_Builder' ) && method_exists( 'WAWP_Flow_Builder', 'get_available_placeholders' ) ) |
| 502 | ? WAWP_Flow_Builder::get_instance()->get_available_placeholders() |
| 503 | : array(); |
| 504 | $data['placeholderGroups'] = ( class_exists( 'WAWP_Flow_Builder' ) && method_exists( 'WAWP_Flow_Builder', 'get_grouped_placeholders' ) ) |
| 505 | ? WAWP_Flow_Builder::get_instance()->get_grouped_placeholders() |
| 506 | : array(); |
| 507 | |
| 508 | // Online Wawp instances for instance picker. |
| 509 | $raw_instances = class_exists( 'WAWP_Instance_Manager' ) ? WAWP()->instance_manager->get_online_instances_cached() : array(); |
| 510 | $online_instances_clean = array(); |
| 511 | if ( ! empty( $raw_instances ) ) { |
| 512 | foreach ( $raw_instances as $inst ) { |
| 513 | $online_instances_clean[] = array( |
| 514 | 'instance_id' => is_object( $inst ) ? $inst->instance_id : ( $inst['instance_id'] ?? '' ), |
| 515 | 'name' => is_object( $inst ) ? $inst->name : ( $inst['name'] ?? '' ), |
| 516 | ); |
| 517 | } |
| 518 | } |
| 519 | $data['onlineInstances'] = $online_instances_clean; |
| 520 | |
| 521 | // Enabled senders for showing active status. |
| 522 | $data['enabledSenders'] = array( |
| 523 | 'wa' => $connector->is_whatsapp_sender_enabled(), |
| 524 | 'email' => $connector->is_email_sender_enabled(), |
| 525 | ); |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | $data = self::decode_translations_recursive( $data ); |
| 530 | return $data; |
| 531 | } catch ( \Exception $e ) { |
| 532 | return array( |
| 533 | 'error' => true, |
| 534 | 'message' => $e->getMessage(), |
| 535 | 'global' => array( 'section' => $section ), |
| 536 | ); |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | /** |
| 541 | * Decode HTML entities recursively in translation arrays. |
| 542 | * |
| 543 | * @param array $data_array Input array. |
| 544 | * @return array Decoded array. |
| 545 | */ |
| 546 | public static function decode_translations_recursive( $data_array ) { |
| 547 | if ( ! is_array( $data_array ) ) { |
| 548 | return $data_array; |
| 549 | } |
| 550 | foreach ( $data_array as $key => $value ) { |
| 551 | if ( 'i18n' === $key || 'status_messages' === $key ) { |
| 552 | $data_array[ $key ] = self::decode_all_strings_recursive( $value ); |
| 553 | } elseif ( is_array( $value ) ) { |
| 554 | $data_array[ $key ] = self::decode_translations_recursive( $value ); |
| 555 | } |
| 556 | } |
| 557 | return $data_array; |
| 558 | } |
| 559 | |
| 560 | /** |
| 561 | * Decode HTML entities recursively for all string values in an array. |
| 562 | * |
| 563 | * @param mixed $value Input value. |
| 564 | * @return mixed Decoded value. |
| 565 | */ |
| 566 | private static function decode_all_strings_recursive( $value ) { |
| 567 | if ( is_array( $value ) ) { |
| 568 | foreach ( $value as $k => $v ) { |
| 569 | $value[ $k ] = self::decode_all_strings_recursive( $v ); |
| 570 | } |
| 571 | } elseif ( is_string( $value ) ) { |
| 572 | $value = html_entity_decode( $value, ENT_QUOTES, 'UTF-8' ); |
| 573 | } |
| 574 | return $value; |
| 575 | } |
| 576 | |
| 577 | |
| 578 | |
| 579 | /** |
| 580 | * Find a Vite-hashed asset file in the dist/assets directory. |
| 581 | * |
| 582 | * Vite generates filenames like index-AbCdEfGh.js. This method |
| 583 | * scans the directory for a file matching the pattern [name]-[hash].[ext] |
| 584 | * so PHP doesn't need to hardcode the hash. |
| 585 | * |
| 586 | * Falls back to [name].[ext] (legacy builds without hash) if not found. |
| 587 | * |
| 588 | * @param string $dir Absolute path to the dist/assets directory (with trailing slash). |
| 589 | * @param string $name Base name without hash (e.g. 'index'). |
| 590 | * @param string $ext File extension without dot (e.g. 'js'). |
| 591 | * @return string The matched filename (basename only), or fallback. |
| 592 | */ |
| 593 | private static function find_hashed_asset( $dir, $name, $ext ) { |
| 594 | if ( is_dir( $dir ) ) { |
| 595 | $pattern = $dir . $name . '-*.' . $ext; |
| 596 | $files = glob( $pattern ); |
| 597 | if ( ! empty( $files ) ) { |
| 598 | // Sort by modification time DESC so the newest build wins. |
| 599 | usort( |
| 600 | $files, |
| 601 | function ( $a, $b ) { |
| 602 | return filemtime( $b ) - filemtime( $a ); |
| 603 | } |
| 604 | ); |
| 605 | return basename( $files[0] ); |
| 606 | } |
| 607 | } |
| 608 | // Fallback: legacy build without hash in filename. |
| 609 | return $name . '.' . $ext; |
| 610 | } |
| 611 | |
| 612 | |
| 613 | |
| 614 | |
| 615 | |
| 616 | /** |
| 617 | * Handle REST API requests for dashboard data fetching. |
| 618 | * |
| 619 | * @param \WP_REST_Request $request REST request object. |
| 620 | * @return \WP_REST_Response REST response. |
| 621 | */ |
| 622 | public static function handle_rest_fetch_data( $request ) { |
| 623 | $section = $request->get_param( 'section' ); |
| 624 | $data = self::prepare_dashboard_data( $section, false ); |
| 625 | $data['nonce'] = wp_create_nonce( 'wawp_nonce' ); |
| 626 | return new WP_REST_Response( $data, 200 ); |
| 627 | } |
| 628 | |
| 629 | /** |
| 630 | * Get the list of languages available/installed on this WordPress site. |
| 631 | * Returns English (default) plus every language whose .mo file is present. |
| 632 | * |
| 633 | * @return array<string, array{locale: string, name: string, nativeName: string, isRtl: bool}> |
| 634 | */ |
| 635 | public static function get_site_available_languages() { |
| 636 | // WordPress RTL locales (partial list covering the most common ones). |
| 637 | $rtl_locales = array( 'ar', 'ar_EG', 'ar_MA', 'ar_SA', 'fa_IR', 'he_IL', 'ug', 'ps', 'sd_PK', 'ku_IQ', 'ckb' ); |
| 638 | |
| 639 | $languages = array(); |
| 640 | |
| 641 | // Always include English as the default fallback. |
| 642 | $languages['en_US'] = array( |
| 643 | 'locale' => 'en_US', |
| 644 | 'name' => 'English (US)', |
| 645 | 'nativeName' => 'English', |
| 646 | 'isRtl' => false, |
| 647 | ); |
| 648 | |
| 649 | // get_available_languages() returns locale slugs that have a .mo file installed. |
| 650 | if ( function_exists( 'get_available_languages' ) ) { |
| 651 | $installed = get_available_languages( WP_LANG_DIR ); |
| 652 | |
| 653 | // Load the WordPress translation installation API if not already loaded. |
| 654 | if ( ! function_exists( 'wp_get_available_translations' ) && file_exists( ABSPATH . 'wp-admin/includes/translation-install.php' ) ) { |
| 655 | require_once ABSPATH . 'wp-admin/includes/translation-install.php'; |
| 656 | } |
| 657 | |
| 658 | // Try to enrich with native names from the translations API cache. |
| 659 | $translations = array(); |
| 660 | if ( function_exists( 'wp_get_available_translations' ) ) { |
| 661 | $translations = wp_get_available_translations(); |
| 662 | } |
| 663 | |
| 664 | // Standard common locales fallback. |
| 665 | $common_locales = array( |
| 666 | 'ar' => array( |
| 667 | 'name' => 'Arabic', |
| 668 | 'nativeName' => 'العربية', |
| 669 | ), |
| 670 | 'ar_EG' => array( |
| 671 | 'name' => 'Arabic (Egypt)', |
| 672 | 'nativeName' => 'العربية (� |
| 673 | صر)', |
| 674 | ), |
| 675 | 'ar_SA' => array( |
| 676 | 'name' => 'Arabic (Saudi Arabia)', |
| 677 | 'nativeName' => 'العربية (السعودية)', |
| 678 | ), |
| 679 | 'fr_FR' => array( |
| 680 | 'name' => 'French (France)', |
| 681 | 'nativeName' => 'Français', |
| 682 | ), |
| 683 | 'fr' => array( |
| 684 | 'name' => 'French', |
| 685 | 'nativeName' => 'Français', |
| 686 | ), |
| 687 | 'es_ES' => array( |
| 688 | 'name' => 'Spanish (Spain)', |
| 689 | 'nativeName' => 'Español', |
| 690 | ), |
| 691 | 'es' => array( |
| 692 | 'name' => 'Spanish', |
| 693 | 'nativeName' => 'Español', |
| 694 | ), |
| 695 | 'de_DE' => array( |
| 696 | 'name' => 'German', |
| 697 | 'nativeName' => 'Deutsch', |
| 698 | ), |
| 699 | 'de' => array( |
| 700 | 'name' => 'German', |
| 701 | 'nativeName' => 'Deutsch', |
| 702 | ), |
| 703 | 'it_IT' => array( |
| 704 | 'name' => 'Italian', |
| 705 | 'nativeName' => 'Italiano', |
| 706 | ), |
| 707 | 'it' => array( |
| 708 | 'name' => 'Italian', |
| 709 | 'nativeName' => 'Italiano', |
| 710 | ), |
| 711 | 'pt_BR' => array( |
| 712 | 'name' => 'Portuguese (Brazil)', |
| 713 | 'nativeName' => 'Português do Brasil', |
| 714 | ), |
| 715 | 'pt' => array( |
| 716 | 'name' => 'Portuguese', |
| 717 | 'nativeName' => 'Português', |
| 718 | ), |
| 719 | 'hi' => array( |
| 720 | 'name' => 'Hindi', |
| 721 | 'nativeName' => 'हिन्दी', |
| 722 | ), |
| 723 | 'ja' => array( |
| 724 | 'name' => 'Japanese', |
| 725 | 'nativeName' => '日本語', |
| 726 | ), |
| 727 | 'ko_KR' => array( |
| 728 | 'name' => 'Korean', |
| 729 | 'nativeName' => '한국어', |
| 730 | ), |
| 731 | 'tr_TR' => array( |
| 732 | 'name' => 'Turkish', |
| 733 | 'nativeName' => 'Türkçe', |
| 734 | ), |
| 735 | 'tr' => array( |
| 736 | 'name' => 'Turkish', |
| 737 | 'nativeName' => 'Türkçe', |
| 738 | ), |
| 739 | 'ru_RU' => array( |
| 740 | 'name' => 'Russian', |
| 741 | 'nativeName' => 'Русский', |
| 742 | ), |
| 743 | 'ru' => array( |
| 744 | 'name' => 'Russian', |
| 745 | 'nativeName' => 'Русский', |
| 746 | ), |
| 747 | 'zh_CN' => array( |
| 748 | 'name' => 'Chinese (China)', |
| 749 | 'nativeName' => '简体中文', |
| 750 | ), |
| 751 | 'zh_TW' => array( |
| 752 | 'name' => 'Chinese (Taiwan)', |
| 753 | 'nativeName' => '繁體中文', |
| 754 | ), |
| 755 | 'nl_NL' => array( |
| 756 | 'name' => 'Dutch', |
| 757 | 'nativeName' => 'Nederlands', |
| 758 | ), |
| 759 | 'nl' => array( |
| 760 | 'name' => 'Dutch', |
| 761 | 'nativeName' => 'Nederlands', |
| 762 | ), |
| 763 | ); |
| 764 | |
| 765 | foreach ( $installed as $locale ) { |
| 766 | if ( 'en_US' === $locale ) { |
| 767 | continue; // Already added. |
| 768 | } |
| 769 | |
| 770 | $lang_prefix = substr( $locale, 0, 2 ); |
| 771 | $is_rtl = in_array( $locale, $rtl_locales, true ) || in_array( $lang_prefix, array( 'ar', 'fa', 'he', 'ur' ), true ); |
| 772 | |
| 773 | if ( isset( $translations[ $locale ] ) ) { |
| 774 | $t = $translations[ $locale ]; |
| 775 | $native_name = ! empty( $t['native_name'] ) ? $t['native_name'] : $locale; |
| 776 | $name = ! empty( $t['english_name'] ) ? $t['english_name'] : $locale; |
| 777 | } elseif ( isset( $common_locales[ $locale ] ) ) { |
| 778 | $native_name = $common_locales[ $locale ]['nativeName']; |
| 779 | $name = $common_locales[ $locale ]['name']; |
| 780 | } elseif ( isset( $common_locales[ $lang_prefix ] ) ) { |
| 781 | $native_name = $common_locales[ $lang_prefix ]['nativeName']; |
| 782 | $name = $common_locales[ $lang_prefix ]['name']; |
| 783 | } else { |
| 784 | // Fallback: pretty-print the locale slug. |
| 785 | $name = str_replace( '_', ' ', $locale ); |
| 786 | $native_name = $name; |
| 787 | } |
| 788 | |
| 789 | $languages[ $locale ] = array( |
| 790 | 'locale' => $locale, |
| 791 | 'name' => $name, |
| 792 | 'nativeName' => $native_name, |
| 793 | 'isRtl' => $is_rtl, |
| 794 | ); |
| 795 | } |
| 796 | } |
| 797 | |
| 798 | return array_values( $languages ); |
| 799 | } |
| 800 | |
| 801 | /** |
| 802 | * AJAX handler: switch the current WordPress admin UI language for this user. |
| 803 | */ |
| 804 | public static function handle_switch_language() { |
| 805 | check_ajax_referer( 'wawp_switch_language', 'nonce' ); |
| 806 | |
| 807 | if ( ! current_user_can( 'manage_options' ) && ! current_user_can( WAWP_CAPABILITY ) ) { |
| 808 | wp_send_json_error( array( 'message' => 'Permission denied.' ), 403 ); |
| 809 | return; |
| 810 | } |
| 811 | |
| 812 | $locale = isset( $_POST['locale'] ) ? sanitize_text_field( wp_unslash( $_POST['locale'] ) ) : ''; |
| 813 | |
| 814 | if ( empty( $locale ) ) { |
| 815 | wp_send_json_error( array( 'message' => 'Invalid locale.' ), 400 ); |
| 816 | return; |
| 817 | } |
| 818 | |
| 819 | // Validate: make sure the locale is actually installed. |
| 820 | $installed = function_exists( 'get_available_languages' ) ? get_available_languages( WP_LANG_DIR ) : array(); |
| 821 | if ( 'en_US' !== $locale && 'en' !== $locale && ! in_array( $locale, $installed, true ) ) { |
| 822 | wp_send_json_error( array( 'message' => 'Locale not available.' ), 400 ); |
| 823 | return; |
| 824 | } |
| 825 | |
| 826 | $user_id = get_current_user_id(); |
| 827 | |
| 828 | if ( 'en' === $locale ) { |
| 829 | $locale = 'en_US'; |
| 830 | } |
| 831 | |
| 832 | // Store the chosen locale in the user meta. |
| 833 | update_user_meta( $user_id, 'locale', $locale ); |
| 834 | |
| 835 | // Clear WordPress internal user locale cache. |
| 836 | wp_cache_delete( $user_id, 'users' ); |
| 837 | |
| 838 | // Bust the WAWP dashboard transient for this user so the next page load |
| 839 | // fetches fresh data (with the correct i18n strings for the new locale). |
| 840 | $cache_key = 'wawp_dashboard_data_full_' . $user_id; |
| 841 | delete_transient( $cache_key ); |
| 842 | wp_cache_delete( $cache_key, 'wawp_database' ); |
| 843 | |
| 844 | // Also clear any tracked transient keys for this user. |
| 845 | $keys = get_option( 'wawp_dashboard_transient_keys', array() ); |
| 846 | if ( is_array( $keys ) ) { |
| 847 | foreach ( $keys as $key ) { |
| 848 | if ( false !== strpos( $key, (string) $user_id ) ) { |
| 849 | delete_transient( $key ); |
| 850 | wp_cache_delete( $key, 'wawp_database' ); |
| 851 | } |
| 852 | } |
| 853 | } |
| 854 | |
| 855 | wp_send_json_success( array( 'locale' => $locale ) ); |
| 856 | } |
| 857 | |
| 858 | /** |
| 859 | * AJAX handler: flush WordPress and React assets cache. |
| 860 | */ |
| 861 | public static function handle_flush_cache() { |
| 862 | check_ajax_referer( 'wawp_flush_cache', 'nonce' ); |
| 863 | |
| 864 | if ( ! current_user_can( 'manage_options' ) && ! current_user_can( WAWP_CAPABILITY ) ) { |
| 865 | wp_send_json_error( array( 'message' => 'Permission denied.' ), 403 ); |
| 866 | return; |
| 867 | } |
| 868 | |
| 869 | // 1. Force bust React cache (updates asset version query args). |
| 870 | update_option( 'wawp_force_cache_bust_version', time(), false ); |
| 871 | |
| 872 | // 2. Clear WordPress Transients & Object Cache. |
| 873 | wp_cache_flush(); |
| 874 | if ( function_exists( 'delete_transient' ) ) { |
| 875 | // Delete specific transients. |
| 876 | delete_transient( 'wawp_last_db_check' ); |
| 877 | delete_transient( 'wawp_online_instances' ); |
| 878 | delete_transient( 'wawp_dashboard_transients' ); |
| 879 | |
| 880 | $user_id = get_current_user_id(); |
| 881 | delete_transient( 'wawp_dashboard_data_full_' . $user_id ); |
| 882 | |
| 883 | // Also delete any other cached dashboard transient keys. |
| 884 | $keys = get_option( 'wawp_dashboard_transient_keys', array() ); |
| 885 | if ( is_array( $keys ) ) { |
| 886 | foreach ( $keys as $key ) { |
| 887 | delete_transient( $key ); |
| 888 | wp_cache_delete( $key, 'wawp_database' ); |
| 889 | } |
| 890 | update_option( 'wawp_dashboard_transient_keys', array(), false ); |
| 891 | } |
| 892 | } |
| 893 | |
| 894 | // 3. OPcache reset if available. |
| 895 | if ( function_exists( 'opcache_reset' ) ) { |
| 896 | opcache_reset(); |
| 897 | } |
| 898 | |
| 899 | // Flush rewrites just in case. |
| 900 | flush_rewrite_rules( false ); |
| 901 | |
| 902 | wp_send_json_success( array( 'message' => 'Cache cleared successfully.' ) ); |
| 903 | } |
| 904 | } |
| 905 | |
| 906 | add_action( 'admin_enqueue_scripts', array( 'WAWP_Enqueue_Scripts', 'enqueue_admin_styles_scripts' ), 999 ); |
| 907 | add_action( 'wp_enqueue_scripts', array( 'WAWP_Enqueue_Scripts', 'enqueue_frontend_styles_scripts' ) ); |
| 908 | add_action( 'wp_ajax_wawp_switch_language', array( 'WAWP_Enqueue_Scripts', 'handle_switch_language' ) ); |
| 909 | add_action( 'wp_ajax_wawp_flush_cache', array( 'WAWP_Enqueue_Scripts', 'handle_flush_cache' ) ); |
| 910 |