| @@ -283,8 +283,15 @@ | ||
| 283 | 283 | } |
| 284 | 284 | |
| 285 | 285 | $sa_connect_url = $dashboard_domain . '/sso/wordpress?' . http_build_query($connect_args); |
| 286 | 286 | |
| 287 | + // Consent-gated analytics: the denominator for searchatlas_connected. | |
| 288 | + $existing_key = Metasync::get_searchatlas_api_key(); | |
| 289 | + Metasync_PostHog::feature('searchatlas_connect_started', array( | |
| 290 | + 'is_reconnection' => is_string($existing_key) && $existing_key !== '', | |
| 291 | + 'is_whitelabel' => (bool) Metasync::is_whitelabel_enabled(), | |
| 292 | + )); | |
| 293 | + | |
| 287 | 294 | wp_send_json_success(array( |
| 288 | 295 | 'connect_url' => $sa_connect_url, |
| 289 | 296 | 'nonce_token' => $sa_connect_token, |
| 290 | 297 | 'debug_info' => array( |
| @@ -459,8 +466,13 @@ | ||
| 459 | 466 | } elseif ($pull['status'] === 'error') { |
| 460 | 467 | // Honest state: surface the real server error (e.g. "Domain not |
| 461 | 468 | // found for customer") instead of the silent 60s timeout. The |
| 462 | 469 | // existing JS branches on status_code (404/500/…) to render it. |
| 470 | + // Once per connect attempt, even if the poll repeats. | |
| 471 | + Metasync_PostHog::feature_once('searchatlas_connect_failed', array( | |
| 472 | + 'status_code' => $pull_status_code ?: 400, | |
| 473 | + 'source' => 'pull', | |
| 474 | + ), md5($nonce_token), 15 * MINUTE_IN_SECONDS); | |
| 463 | 475 | wp_send_json_success(array( |
| 464 | 476 | 'updated' => true, |
| 465 | 477 | 'status_code' => $pull_status_code ?: 400, |
| 466 | 478 | 'message' => isset($connect['message']) ? sanitize_text_field($connect['message']) : '', |
| @@ -1076,18 +1088,16 @@ | ||
| 1076 | 1088 | |
| 1077 | 1089 | $cleaned_tokens = $this->cleanup_searchatlas_nonce_tokens(); |
| 1078 | 1090 | $cleared_data['sa_connect_nonce_tokens'] = 'none (simplified token system)'; |
| 1079 | 1091 | |
| 1080 | - delete_option(Metasync::option_name . '_whitelabel_user'); | |
| 1081 | - $cleared_data['whitelabel_user'] = 'removed'; | |
| 1092 | + // White-label identity is preserved across the authentication | |
| 1093 | + // reset: the reseller's branding, logos, domains, custom OTTO | |
| 1094 | + // name, visibility/access controls, password and recovery | |
| 1095 | + // settings, and the dedicated whitelabel_user option must survive | |
| 1096 | + // the disconnect. Only authentication tokens and credentials are | |
| 1097 | + // cleared above. Do not delete the whitelabel blob or the | |
| 1098 | + // whitelabel_user option here. | |
| 1082 | 1099 | |
| 1083 | - if (isset($options['whitelabel'])) { | |
| 1084 | - $cleared_data['whitelabel_settings'] = 'removed'; | |
| 1085 | - unset($options['whitelabel']); | |
| 1086 | - | |
| 1087 | - $this->write_options_without_stored_merge($options); | |
| 1088 | - } | |
| 1089 | - | |
| 1090 | 1100 | $this->clear_jwt_token_cache(); |
| 1091 | 1101 | $this->revoke_mcp_jwt_tokens(); |
| 1092 | 1102 | $cleared_data['jwt_token_cache'] = 'cleared'; |
| 1093 | 1103 | |
| @@ -1110,8 +1120,30 @@ | ||
| 1110 | 1120 | $cleared_data['heartbeat_cache'] = 'cleared'; |
| 1111 | 1121 | $cleared_data['last_known_connection_state'] = 'cleared'; |
| 1112 | 1122 | |
| 1113 | 1123 | Metasync_Heartbeat_Manager::instance()->unschedule_heartbeat_cron(); |
| 1124 | + | |
| 1125 | + // Consent-gated analytics: churn signal, the opposite of connected. | |
| 1126 | + Metasync_PostHog::feature('searchatlas_disconnected', array( | |
| 1127 | + 'method' => 'reset_auth', | |
| 1128 | + 'was_connected' => isset($cleared_data['searchatlas_api_key']), | |
| 1129 | + )); | |
| 1130 | + | |
| 1131 | + // Re-synchronise the branded plugin file headers so the WordPress | |
| 1132 | + // plugin list and the plugin UI stay branded after the | |
| 1133 | + // authentication reset. Only attempted when white-label branding | |
| 1134 | + // exists, and a failed sync is logged as a warning, not reported | |
| 1135 | + // as a reset failure: the credentials have already been cleared, | |
| 1136 | + // and hosts with read-only plugin files would otherwise never see | |
| 1137 | + // a successful reset. | |
| 1138 | + if (Metasync::is_whitelabel_enabled()) { | |
| 1139 | + if (!class_exists('Metasync_Activator')) { | |
| 1140 | + require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-activator.php'; | |
| 1141 | + } | |
| 1142 | + if (!Metasync_Activator::sync_plugin_file_headers()) { | |
| 1143 | + error_log('MetaSync: failed to re-synchronise branded plugin file headers after the authentication reset.'); | |
| 1144 | + } | |
| 1145 | + } | |
| 1114 | 1146 | |
| 1115 | 1147 | wp_send_json_success(array( |
| 1116 | 1148 | 'message' => 'Authentication has been reset successfully. You can now connect a new account.', |
| 1117 | 1149 | 'cleared_data' => $cleared_data, |