File manager - Edit - /home/wwwwaterjetperth/public_html/wp-content/plugins/microsoft-clarity/clarity-page.php
Back
<?php /******************************************************************************* * File with Clarity page *******************************************************************************/ // Handle Brand Agent remove from waitlist success callback - use add_action to ensure WordPress is loaded add_action( 'init', 'brandagent_handle_remove_from_waitlist_success_callback', 1 ); function brandagent_handle_remove_from_waitlist_success_callback() { if ( isset( $_GET['brandagent_remove_from_waitlist_success'] ) && $_GET['brandagent_remove_from_waitlist_success'] == '1' ) { brandagent_log( 'BrandAgent: Received remove from waitlist success callback' ); // Verify HMAC signature from request headers $client_id = isset( $_SERVER['HTTP_X_WOOCOMMERCE_CLIENT_ID'] ) ? sanitize_text_field( $_SERVER['HTTP_X_WOOCOMMERCE_CLIENT_ID'] ) : ''; $timestamp = isset( $_SERVER['HTTP_X_WOOCOMMERCE_TIMESTAMP'] ) ? sanitize_text_field( $_SERVER['HTTP_X_WOOCOMMERCE_TIMESTAMP'] ) : ''; $signature = isset( $_SERVER['HTTP_X_WOOCOMMERCE_SIGNATURE'] ) ? sanitize_text_field( $_SERVER['HTTP_X_WOOCOMMERCE_SIGNATURE'] ) : ''; // Validate required headers are present if ( empty( $client_id ) || empty( $timestamp ) || empty( $signature ) ) { brandagent_log( 'BrandAgent: Remove from waitlist callback missing required HMAC headers' ); header( 'Content-Type: application/json' ); http_response_code( 401 ); echo json_encode( array( 'success' => false, 'error' => 'Missing authentication headers' ) ); exit; } // Validate timestamp (5-minute window for replay attack prevention) $time_difference = abs( time() - intval( $timestamp ) ); if ( $time_difference > 300 ) { brandagent_log( 'BrandAgent: Remove from waitlist callback timestamp too old: ' . $time_difference . ' seconds' ); header( 'Content-Type: application/json' ); http_response_code( 401 ); echo json_encode( array( 'success' => false, 'error' => 'Request timestamp expired' ) ); exit; } // Get the stored HMAC secret and verify signature $secret_key = brandagent_get_hmac_secret(); if ( ! $secret_key ) { brandagent_log( 'BrandAgent: Remove from waitlist callback - no HMAC secret stored' ); header( 'Content-Type: application/json' ); http_response_code( 401 ); echo json_encode( array( 'success' => false, 'error' => 'HMAC secret not found' ) ); exit; } // Compute expected signature: message = clientId + timestamp $expected_signature = brandagent_generate_hmac_signature( $client_id, $timestamp, $secret_key ); // Constant-time comparison to prevent timing attacks if ( ! hash_equals( $expected_signature, $signature ) ) { brandagent_log( 'BrandAgent: Remove from waitlist callback HMAC signature verification failed' ); header( 'Content-Type: application/json' ); http_response_code( 401 ); echo json_encode( array( 'success' => false, 'error' => 'Invalid signature' ) ); exit; } brandagent_log( 'BrandAgent: Remove from waitlist callback HMAC signature verified successfully' ); delete_option( 'BAOauthSuccess' ); delete_option( 'BAInjectFrontendScript' ); delete_option( 'BAOauthRepairDone' ); delete_option( 'BAWebhooksCreated' ); delete_option( 'BAWebhooksBackfillDone' ); delete_option( 'clarity_ba_eligible_triggered' ); brandagent_delete_hmac_secret(); // Try to delete webhooks immediately if WooCommerce is available $deleted_immediately = false; if ( class_exists( 'WooCommerce' ) && class_exists( 'BrandAgent_Webhooks' ) ) { $deleted_count = BrandAgent_Webhooks::delete_all_brandagent_webhooks(); brandagent_log( 'BrandAgent: Deleted ' . $deleted_count . ' webhook(s) immediately' ); $deleted_immediately = true; } // If WooCommerce not loaded, set transient for later deletion if ( ! $deleted_immediately ) { set_transient( 'brandagent_pending_webhook_deletion', true, 3600 ); brandagent_log( 'BrandAgent: Set pending webhook deletion transient (WooCommerce not loaded)' ); } header( 'Content-Type: application/json' ); echo json_encode( array( 'success' => true ) ); exit; } } // Handle WooCommerce OAuth return URL (browser redirect after user authorizes). // Deferred to 'init' so that wp_remote_post() and the HTTP API are fully loaded. add_action( 'init', 'brandagent_handle_oauth_callback', 1 ); function brandagent_handle_oauth_callback() { if ( ! isset($_GET['brandagent_callback']) || $_GET['brandagent_callback'] != '1' ) { return; } $oauth_token = isset($_GET['oauth_token']) ? sanitize_text_field($_GET['oauth_token']) : ''; // WooCommerce adds success=1 to the return URL when the callback was successful $wc_success = isset($_GET['success']) && $_GET['success'] == '1'; $success = false; brandagent_log( 'BrandAgent OAuth: Callback received', array( 'wc_success' => $wc_success, 'oauth_present' => ! empty( $oauth_token ), ) ); if ($wc_success && !empty($oauth_token)) { // Pull the HMAC secret from Clarity server (server-to-server, secret in response body) if ( ! class_exists( 'BrandAgent_Config' ) ) { $config_path = plugin_dir_path( __FILE__ ) . 'includes/brandagent-config.php'; if ( file_exists( $config_path ) ) { require_once $config_path; } else { brandagent_log( 'BrandAgent OAuth: ERROR - Config file not found at ' . $config_path ); } } $clarity_server_url = BrandAgent_Config::get_clarity_server_url(); $fetch_url = $clarity_server_url . '/woocommerce/fetch-secret'; $request_body = wp_json_encode( array( 'oauth_token' => $oauth_token ) ); $response = wp_remote_post( $fetch_url, array( 'timeout' => 15, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => $request_body, ) ); if ( is_wp_error( $response ) ) { brandagent_log( 'BrandAgent OAuth: ERROR - wp_remote_post failed: ' . $response->get_error_message() ); } else { $status_code = wp_remote_retrieve_response_code( $response ); $response_body = wp_remote_retrieve_body( $response ); if ( $status_code === 200 ) { $body = json_decode( $response_body, true ); if ( json_last_error() !== JSON_ERROR_NONE ) { brandagent_log( 'BrandAgent OAuth: ERROR - JSON parse failed: ' . json_last_error_msg() ); } elseif ( isset( $body['success'] ) && $body['success'] === true && ! empty( $body['hmac_secret'] ) ) { $hmac_secret_stored = brandagent_store_hmac_secret( $body['hmac_secret'] ); update_option( 'BAOauthSuccess', true ); $success = true; brandagent_log( 'BrandAgent OAuth: SUCCESS - HMAC secret handled, BAOauthSuccess set.', array( 'hmac_stored' => $hmac_secret_stored ) ); } else { brandagent_log( 'BrandAgent OAuth: ERROR - Unexpected response from fetch-secret', array( 'status_code' => $status_code, 'success_present' => isset( $body['success'] ), 'hmac_present' => isset( $body['hmac_secret'] ) && ! empty( $body['hmac_secret'] ), ) ); } } else { brandagent_log( 'BrandAgent OAuth: ERROR - fetch-secret returned non-success status', array( 'status_code' => $status_code ) ); } } } else { brandagent_log( 'BrandAgent OAuth: SKIPPED - WooCommerce success or OAuth token missing', array( 'wc_success' => $wc_success, 'oauth_present' => ! empty( $oauth_token ), ) ); } ?> <!DOCTYPE html> <html> <body> <script> if (window.opener) { <?php if ($success): ?> window.opener.postMessage({ type: 'WOOCOMMERCE_OAUTH_SUCCESS' }, '*'); <?php else: ?> window.opener.postMessage({ type: 'WOOCOMMERCE_OAUTH_FAILURE' }, '*'); <?php endif; ?> setTimeout(function() { window.close(); }, 100); } </script> </body> </html> <?php exit; } // Handle Brand Agent refresh credentials callback add_action( 'init', 'brandagent_handle_refresh_credentials_callback', 1 ); function brandagent_handle_refresh_credentials_callback() { if ( ! isset( $_GET['brandagent_refresh_credentials'] ) || $_GET['brandagent_refresh_credentials'] != '1' ) { return; } $oauth_token = isset( $_GET['oauth_token'] ) ? sanitize_text_field( $_GET['oauth_token'] ) : ''; $success = false; brandagent_log( 'BrandAgent Refresh: Callback received', array( 'oauth_present' => ! empty( $oauth_token ) ) ); if ( ! empty( $oauth_token ) ) { // Ensure BrandAgent_Config is loaded if ( ! class_exists( 'BrandAgent_Config' ) ) { $config_path = plugin_dir_path( __FILE__ ) . 'includes/brandagent-config.php'; if ( file_exists( $config_path ) ) { require_once $config_path; } else { brandagent_log( 'BrandAgent Refresh: ERROR - Config file not found at ' . $config_path ); } } // Fetch the new HMAC secret from Clarity server using the opaque token $clarity_server_url = BrandAgent_Config::get_clarity_server_url(); $fetch_url = $clarity_server_url . '/woocommerce/fetch-secret'; $request_body = wp_json_encode( array( 'oauth_token' => $oauth_token ) ); $response = wp_remote_post( $fetch_url, array( 'timeout' => 15, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => $request_body, ) ); if ( is_wp_error( $response ) ) { brandagent_log( 'BrandAgent Refresh: ERROR - wp_remote_post failed: ' . $response->get_error_message() ); } else { $status_code = wp_remote_retrieve_response_code( $response ); $response_body = wp_remote_retrieve_body( $response ); if ( $status_code === 200 ) { $body = json_decode( $response_body, true ); if ( json_last_error() !== JSON_ERROR_NONE ) { brandagent_log( 'BrandAgent Refresh: ERROR - JSON parse failed: ' . json_last_error_msg() ); } elseif ( isset( $body['success'] ) && $body['success'] === true && ! empty( $body['hmac_secret'] ) ) { $hmac_secret_stored = brandagent_store_hmac_secret( $body['hmac_secret'] ); $success = true; brandagent_log( 'BrandAgent Refresh: SUCCESS - New HMAC secret handled.', array( 'hmac_stored' => $hmac_secret_stored ) ); } else { brandagent_log( 'BrandAgent Refresh: ERROR - Unexpected response from fetch-secret', array( 'status_code' => $status_code, 'success_present' => isset( $body['success'] ), 'hmac_present' => isset( $body['hmac_secret'] ) && ! empty( $body['hmac_secret'] ), ) ); } } else { brandagent_log( 'BrandAgent Refresh: ERROR - fetch-secret returned non-success status', array( 'status_code' => $status_code ) ); } } } else { brandagent_log( 'BrandAgent Refresh: ERROR - Missing oauth_token parameter' ); } header( 'Content-Type: application/json' ); if ( $success ) { echo json_encode( array( 'success' => true ) ); } else { http_response_code( 500 ); echo json_encode( array( 'success' => false, 'error' => 'Failed to refresh credentials' ) ); } exit; } function generate_wordpress_id_option_if_empty() { $clarity_wp_site = get_option('clarity_wordpress_site_id'); if (empty($clarity_wp_site)) { update_option('clarity_wordpress_site_id', wp_generate_uuid4()); }; } /** * generate a guid identifier for this wordpress site * runs in the callback of register_activation_hook, rerunning here for existing plugin which updated **/ function refresh_wordpress_id_option() { update_option('clarity_wordpress_site_id', wp_generate_uuid4()); } /** * Detects whether this site is hosted on WordPress.com. **/ function clarity_is_wordpress_com_hosted() { return defined('IS_WPCOM') && IS_WPCOM; } /** * Displays the embedded iframe in Clarity settings **/ function clarity_section_iframe_callback() { $nonce = wp_create_nonce('wp_ajax_edit_clarity_project_id'); $clarity_project_id_option = get_option( 'clarity_project_id', /* option */ clarity_project_id_default_value() /* default */ ); $clarity_wp_site = get_option( 'clarity_wordpress_site_id' /* option */ /* default */ ); $site_url = home_url(); $hosting_type = clarity_is_wordpress_com_hosted() ? 'wpcom' : 'selfhosted'; $clarity_domain = "https://clarity.microsoft.com/embed"; $query_params = "?nonce=$nonce&integration=Wordpress&wpsite=$clarity_wp_site&siteurl=$site_url&hostingtype=$hosting_type"; // set a QP if user is admin if (current_user_can('manage_options')) { $query_params = $query_params . "&WPAdmin=1"; } // set a QP if user is WooCommerce plugin is active if (class_exists('woocommerce')) { $query_params = $query_params . "&WooCommerce=1"; // Trigger RAI eligibility check on BA server (fire-and-forget, once per install) if ( get_option( 'clarity_ba_eligible_triggered', '' ) === '' ) { clarity_trigger_ba_eligibility( $site_url ); } } // set a QP if permalink structure is plain (required for Brand Agent rewrite rules) if (get_option('permalink_structure') === '') { $query_params = $query_params . "&PlainPermalink=1"; } // Add flag to indicate Brand Agent integration is supported (0.10.21+) // If this flag is missing, iframe knows user is on an older version $query_params = $query_params . "&BrandAgentSupported=1"; // initially set iframe src to the new users path $iframe_src = $clarity_domain . $query_params; // clarity project exist if (!empty($clarity_project_id_option)) { $iframe_src = $iframe_src . "&project=" . $clarity_project_id_option; } // Support deep-linking to specific pages in the embedded Clarity dashboard if (isset($_GET['iframeRedirect']) && !empty($_GET['iframeRedirect'])) { $iframe_redirect = sanitize_text_field($_GET['iframeRedirect']); $iframe_src = $iframe_src . "&iframeRedirect=" . rawurlencode($iframe_redirect); } ?> <div style="width:100%;height:100vh;padding-right:15px;margin-top:0px;box-sizing:border-box;"> <iframe sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups allow-storage-access-by-user-activation" src="<?php echo $iframe_src ?>" width="100%" height="100%" title="Microsoft Clarity" /> </div> <?php } /** * clarity project id default value is empty string **/ function clarity_project_id_default_value() { return ''; } /** * Generates a menu page **/ add_action('admin_menu', 'clarity_page_generation'); function clarity_page_generation() { add_menu_page( 'microsoft-clarity', /* $page_title */ 'Clarity', /* menu_title */ 'edit_posts', /* capability */ 'microsoft-clarity', /* menu_slug */ 'clarity_section_iframe_callback', /* callback */ 'https://claritystatic.blob.core.windows.net/images/logo.svg', /* icon_url */ 99 /* position */ ); } /** * Register Plugin settings * clarity_project_id: option for currently integrated Clarity project id **/ add_action('admin_init', 'clarity_register_settings'); function clarity_register_settings() { register_setting( 'clarity_settings_fields', /* $option_group */ 'clarity_project_id' /* option_name */ /* args */ ); } /** * Notice for when wordpress admins did not finish intalling Clarity * did not integrate a project */ add_action('admin_notices', 'setup_clarity_notice__info'); function setup_clarity_notice__info() { global $pagenow; $url = get_admin_url() . 'admin.php?page=microsoft-clarity'; $learnMoreUrl = 'https://wordpress.org/plugins/microsoft-clarity/'; $clarity_project_id_option = get_option( 'clarity_project_id', /* option */ clarity_project_id_default_value() /* default */ ); $pageQPExists = isset($_GET['page']); if ($pageQPExists) { $pageQP = $_GET['page']; } else { $pageQP = ""; } if (empty($clarity_project_id_option) && $pageQP !== "microsoft-clarity" && current_user_can("manage_options")) { echo '<div class="notice notice-info is-dismissible"> <p style="font-weight:700"> Unlock User Insights with Microsoft Clarity! </p> <p style="font-weight:500"> Almost there! Start tracking user behavior on your site with Microsoft Clarity. See exactly where on your site users click, scroll, and get stuck. It takes just a few moments to set up. </p> <p> <a class="button-primary" href="' . $url . '"> Setup Clarity </a> <a class="button-primary" style="margin-left:10px" href="' . $learnMoreUrl . '"> Learn more </a> </p> </div>'; } } /** * Add js function to listen to message on all admin pages * These message contain changes to integrated Clarity project * remove - change - add new */ add_action('admin_enqueue_scripts', 'add_event_listeners'); function add_event_listeners($hook) { $pageQPExists = isset($_GET['page']); if ($pageQPExists) { $pageQP = $_GET['page']; } else { $pageQP = ""; } if ($pageQP !== "microsoft-clarity") { return; } if (!current_user_can("edit_posts")) { return; } wp_register_script( 'window_listeners_js', /* handle */ plugins_url('js\add_window_listeners.js', __FILE__), /* src */ array(), /* deps */ false, /* ver */ false /* in_footer */ ); wp_enqueue_script( 'window_listeners_js' /* handle */ /* src */ /* deps */ /* ver */ /* in_footer */ ); } /** * Add callback triggered when a new message is received * Edits the clarity project id option respectively */ add_action('wp_ajax_edit_clarity_project_id', "edit_clarity_project_id"); function edit_clarity_project_id() { $new_value = $_POST['new_value']; $nonce = $_POST['nonce']; if (!wp_verify_nonce($nonce, "wp_ajax_edit_clarity_project_id")) { die(json_encode( array( 'success' => false, 'message' => 'Invalid nonce.', ) )); } // only admins are allowed to edit the Clarity project id if (!current_user_can('manage_options')) { die(json_encode( array( 'success' => false, 'message' => 'User must be WordPress admin.' ) )); } else { update_option( 'clarity_project_id', /* option */ $new_value /* value */ /* autoload */ ); die(json_encode( array( 'success' => true, 'message' => 'Clarity project updated successfully.' ) )); } } /** * Add callback triggered when a new message is received * Edits the agent enabled status option respectively */ add_action('wp_ajax_edit_agent_enabled_status', "edit_agent_enabled_status"); function edit_agent_enabled_status() { $new_value = $_POST['new_value']; $nonce = $_POST['nonce']; if (!wp_verify_nonce($nonce, "wp_ajax_edit_clarity_project_id")) { die(json_encode( array( 'success' => false, 'message' => 'Invalid nonce.', ) )); } // only admins are allowed to edit the Clarity project id if (!current_user_can('manage_options')) { die(json_encode( array( 'success' => false, 'message' => 'User must be WordPress admin.' ) )); } else { update_option( 'BAOauthSuccess', /* option */ $new_value /* value */ /* autoload */ ); die(json_encode( array( 'success' => true, 'message' => 'Agent enabled status updated successfully.' ) )); } } /** * Displays an admin notice if the plugin version installed is not the latest */ add_action('admin_notices', 'plugin_update_notice'); function plugin_update_notice() { // Only show the notice to users who can update plugins if (! current_user_can('update_plugins')) { return; } $is_latest_version = get_transient('clarity_is_latest_plugin_version'); if ($is_latest_version !== '0') { return; } $plugin_slug = 'microsoft-clarity/clarity.php'; $update_url = wp_nonce_url( add_query_arg( array( 'action' => 'trigger_plugin_update', 'plugin' => urlencode($plugin_slug), ), admin_url('admin.php') ), 'plugin_update_nonce' ); ?> <div class="notice notice-warning is-dismissible"> <p style="font-weight:700"> <?php _e('A new version of Microsoft Clarity is available.', 'text-domain'); ?> </p> <p> <a href="<?php echo esc_url($update_url); ?>" class="button button-primary"> <?php _e('Update Now', 'text-domain'); ?> </a> </p> </div> <?php } /** * Updates the plugin to the latest version programmatically * The upgrade function deactives the plugin by default before the upgrade, hence the need to reactivate it */ add_action('admin_action_trigger_plugin_update', 'plugin_perform_update'); function plugin_perform_update() { if (! current_user_can('update_plugins')) { wp_die(__('You do not have sufficient permissions to update plugins.', 'text-domain')); } if (! isset($_GET['plugin']) || ! isset($_GET['_wpnonce'])) { return; } $plugin_slug = sanitize_text_field(urldecode($_GET['plugin'])); if (! wp_verify_nonce($_GET['_wpnonce'], 'plugin_update_nonce')) { wp_die(__('Nonce verification failed.', 'text-domain')); } include_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'); include_once(ABSPATH . 'wp-admin/includes/plugin.php'); // Create a custom skin to handle output and redirection $upgrader_skin = new Automatic_Upgrader_Skin(); $upgrader = new Plugin_Upgrader($upgrader_skin); // Perform the update $updated = $upgrader->upgrade($plugin_slug); if (is_wp_error($updated) || ! $updated) { // Handle error: redirect back to admin page with an error notice $redirect_url = add_query_arg('plugin_update_error', '1', admin_url('plugins.php')); wp_redirect(esc_url($redirect_url)); exit; } else { // Success: redirect back to the plugin page with a success notice activate_plugin($plugin_slug); $redirect_url = add_query_arg('plugin_updated', '1', admin_url('admin.php?page=microsoft-clarity')); wp_redirect(esc_url($redirect_url)); exit; } } /** * Display an admin notice with the status of the plugin update */ add_action('admin_notices', 'plugin_admin_notices'); function plugin_admin_notices() { if (isset($_GET['plugin_updated']) && '1' === $_GET['plugin_updated']) { echo '<div class="notice notice-success is-dismissible"> <p><strong>Microsoft Clarity plugin has been updated successfully.</strong></p> </div>'; } else if (isset($_GET['plugin_update_error']) && '1' === $_GET['plugin_update_error']) { echo '<div class="notice notice-error is-dismissible"> <p><strong>Microsoft Clarity plugin update failed.</strong></p> </div>'; } } /** * Trigger the Brand Agent eligibility (RAI) check via Clarity server proxy. * Fire-and-forget — the plugin doesn't need the result. The BA server caches it * for the Clarity Dashboard to read later. Uses a non-blocking request so the * admin page isn't delayed. */ function clarity_trigger_ba_eligibility( $store_url ) { if ( ! class_exists( 'BrandAgent_Config' ) ) { $config_path = plugin_dir_path( __FILE__ ) . 'includes/brandagent-config.php'; if ( file_exists( $config_path ) ) { require_once $config_path; } else { brandagent_log( 'BrandAgent Eligibility: ERROR - Config file not found at ' . $config_path ); return; } } $clarity_server_url = BrandAgent_Config::get_clarity_server_url(); $endpoint = $clarity_server_url . '/woocommerce/is-eligible'; brandagent_log( 'BrandAgent Eligibility: triggering check for ' . $store_url . ' via ' . $endpoint ); // Non-blocking: we don't need the response — just need to trigger the request // so the BA server starts the RAI check. $response = wp_remote_post( $endpoint, array( 'timeout' => 0.01, 'blocking' => false, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( array( 'storeUrl' => $store_url ) ), ) ); if ( is_wp_error( $response ) ) { brandagent_log( 'BrandAgent Eligibility: ERROR - failed to trigger check', array( 'error' => $response->get_error_message() ) ); } // Mark as triggered so we don't re-fire on every admin page load update_option( 'clarity_ba_eligible_triggered', '1' ); brandagent_log( 'BrandAgent Eligibility: marked eligibility trigger as completed', array( 'store_url' => $store_url ) ); }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0 |
proxy
|
phpinfo
|
Settings