PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.2
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.2
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
← All changes | includes/class-metasync-admin-ajax.php +80 -34 2.7.1 → 2.7.2 View file →
@@ -168,8 +168,17 @@
168 168 $result = $importer->import_schema($plugin);
169 169 break;
170 170 }
171 171
172 + // A schema import that only found already-migrated or unsupported
173 + // records is an accurate report, not a failure — keep the breakdown
174 + // with the response so the UI can show each category separately.
175 + if (isset($result['breakdown'])) {
176 + $result['breakdown'] = array_map('intval', (array) $result['breakdown']);
177 + }
178 +
179 + Metasync_PostHog::import_batch($plugin, $type, $result, $offset);
180 +
172 181 if ($result['success']) {
173 182 wp_send_json_success($result);
174 183 } else {
175 184 wp_send_json_error($result);
@@ -217,8 +226,12 @@
217 226 ];
218 227
219 228 $result = $importer->import_seo_metadata($plugin, $options);
220 229
230 + Metasync_PostHog::import_batch($plugin, 'seo_metadata', $result, $offset, array(
231 + 'overwrite_existing' => $overwrite_existing,
232 + ));
233 +
221 234 if ($result['success']) {
222 235 wp_send_json_success($result);
223 236 } else {
224 237 wp_send_json_error($result);
@@ -319,9 +332,9 @@
319 332 $step = isset($_POST['step']) ? intval($_POST['step']) : 0;
320 333 $raw_data = isset($_POST['data']) ? $_POST['data'] : array();
321 334
322 335 // Allowlist top-level wizard data keys to prevent mass assignment
323 - $allowed_wizard_keys = array('verification', 'seo_settings', 'schema');
336 + $allowed_wizard_keys = array('verification', 'seo_settings', 'schema', 'analytics');
324 337 $data = is_array($raw_data) ? array_intersect_key($raw_data, array_flip($allowed_wizard_keys)) : array();
325 338
326 339 $options = get_option('metasync_options', array());
327 340
@@ -351,8 +364,13 @@
351 364 $options['general']['enable_schema_markup'] = $data['schema']['enabled'];
352 365 $options['general']['default_schema_type'] = sanitize_text_field($data['schema']['default_type']);
353 366 }
354 367
368 + if (isset($data['analytics']) && is_array($data['analytics'])) {
369 + update_option('metasync_analytics_opt_in', !empty($data['analytics']['enabled']) ? 'yes' : 'no');
370 + update_option('metasync_analytics_consent_explicit', 'yes');
371 + }
372 +
355 373 update_option('metasync_options', $options);
356 374
357 375 wp_send_json_success(array('message' => 'Progress saved'));
358 376 }
@@ -374,8 +392,14 @@
374 392
375 393 $user_id = get_current_user_id();
376 394 delete_transient("metasync_wizard_state_{$user_id}");
377 395
396 + // Consent-gated product analytics: setup funnel completion.
397 + $general_settings = Metasync::get_option('general');
398 + Metasync_PostHog::feature('setup_wizard_completed', array(
399 + 'connected' => !empty($general_settings['searchatlas_api_key']),
400 + ));
401 +
378 402 wp_send_json_success(array('message' => 'Wizard completed'));
379 403 }
380 404
381 405 public function ajax_validate_robots()
@@ -688,8 +712,43 @@
688 712 error_log('MetaSync: Transient cleanup failed - ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
689 713 }
690 714 }
691 715
716 + /**
717 + * Collect the locally stored white-label settings for an issue-report payload.
718 + *
719 + * The values are attached to the Sentry event so support can tell from the
720 + * event alone whether the reporting site is white-labeled. Local options
721 + * can be stale — the reply-drafting process must still cross-check the
722 + * backend account record before sending.
723 + *
724 + * Static and public so the unit suite can exercise the reads directly; the
725 + * AJAX handler itself dies on wp_send_json_* before returning.
726 + *
727 + * @return array Keys: is_whitelabel, whitelabel_brand_name, whitelabel_support_email, site_url.
728 + */
729 + public static function build_issue_report_whitelabel_context()
730 + {
731 + $whitelabel_settings = Metasync::get_whitelabel_settings();
732 + if (!is_array($whitelabel_settings)) {
733 + $whitelabel_settings = array();
734 + }
735 + $whitelabel_support_email = '';
736 + foreach (array('support_email_override', 'support_email', 'recovery_email') as $email_key) {
737 + if (!empty($whitelabel_settings[$email_key])) {
738 + $whitelabel_support_email = sanitize_email($whitelabel_settings[$email_key]);
739 + break;
740 + }
741 + }
742 + return array(
743 + 'is_whitelabel' => Metasync::is_whitelabel_enabled(),
744 + 'whitelabel_brand_name' => !empty($whitelabel_settings['company_name'])
745 + ? sanitize_text_field($whitelabel_settings['company_name']) : '',
746 + 'whitelabel_support_email' => $whitelabel_support_email,
747 + 'site_url' => esc_url_raw(home_url()),
748 + );
749 + }
750 +
692 751 public function ajax_submit_issue_report()
693 752 {
694 753 try {
695 754 # Verify nonce
@@ -760,8 +819,11 @@
760 819 );
761 820 }
762 821 }
763 822
823 + # Read the locally stored white-label settings for the report payload.
824 + $whitelabel_context = self::build_issue_report_whitelabel_context();
825 +
764 826 # Get general options (same way as used throughout the plugin)
765 827 $general_options = Metasync::get_option('general');
766 828 if (!is_array($general_options)) {
767 829 $general_options = array();
@@ -821,9 +883,13 @@
821 883 }
822 884 } else {
823 885 $feedback_data = array(
824 886 'message' => $issue_message,
825 - 'severity' => $issue_severity
887 + 'severity' => $issue_severity,
888 + 'is_whitelabel' => $whitelabel_context['is_whitelabel'],
889 + 'whitelabel_brand_name' => $whitelabel_context['whitelabel_brand_name'],
890 + 'whitelabel_support_email' => $whitelabel_context['whitelabel_support_email'],
891 + 'site_url' => $whitelabel_context['site_url']
826 892 );
827 893
828 894 # Add user information if requested
829 895 if ($include_user_info) {
@@ -1214,40 +1280,20 @@
1214 1280 wp_die('Security verification failed.');
1215 1281 }
1216 1282
1217 1283 try {
1218 - # Get all whitelabel settings
1219 - $whitelabel_settings = Metasync::get_whitelabel_settings();
1220 -
1221 - # Export the settings password as plaintext (not the encrypted blob):
1222 - # the encryption key derives from this site's salts, so the blob
1223 - # would be undecryptable after import on a different site. The
1224 - # importer re-encrypts it at rest with the destination site's salts.
1225 - if (!empty($whitelabel_settings['settings_password'])) {
1226 - $whitelabel_settings['settings_password'] = Metasync::get_whitelabel_password();
1284 + # Source the white-label export payload from the shared helper so
1285 + # reset and export can never drift apart: whatever the reset
1286 + # preserves is exactly what the export serialises. No API
1287 + # credentials (apikey / searchatlas_api_key / otto_pixel_uuid / ...)
1288 + # are ever serialised - the helper only captures the whitelabel
1289 + # blob and the general branding keys.
1290 + if (!class_exists('Metasync_Whitelabel_Preservation')) {
1291 + require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-whitelabel-preservation.php';
1227 1292 }
1228 -
1229 - # Get general settings that relate to whitelabel
1230 - $general_settings = Metasync::get_option('general');
1231 - $whitelabel_related_general = array();
1232 -
1233 - # Include ALL whitelabel-related general settings
1234 - $whitelabel_keys = array(
1235 - 'white_label_plugin_name',
1236 - 'white_label_plugin_description',
1237 - 'white_label_plugin_author',
1238 - 'white_label_plugin_author_uri',
1239 - 'white_label_plugin_uri',
1240 - 'white_label_plugin_menu_slug',
1241 - 'white_label_plugin_menu_icon',
1242 - 'whitelabel_otto_name'
1243 - );
1244 -
1245 - foreach ($whitelabel_keys as $key) {
1246 - if (isset($general_settings[$key])) {
1247 - $whitelabel_related_general[$key] = $general_settings[$key];
1248 - }
1249 - }
1293 + $export_payload = Metasync_Whitelabel_Preservation::build_whitelabel_export_data();
1294 + $whitelabel_settings = $export_payload['whitelabel_settings'];
1295 + $whitelabel_related_general = $export_payload['general_settings'];
1250 1296
1251 1297 # Bundle the menu icon file so it survives import on a different site.
1252 1298 # If the icon is a local URL (media library), replace it with a special
1253 1299 # marker and include the actual file in the ZIP under the plugin folder.
@@ -1449,9 +1495,9 @@
1449 1495 echo '<div class="metasync-dashboard-widget-empty">';
1450 1496 echo '<span class="dashicons dashicons-admin-page" style="font-size: 48px; opacity: 0.3; display: block; margin: 20px auto;"></span>';
1451 1497 echo '<p style="text-align: center; color: #666;">';
1452 1498 /* translators: %s: HTML source label. */
1453 - echo sprintf(__('No pages created with %s yet.', 'metasync'), '<strong>' . esc_html($label) . '</strong>');
1499 + echo sprintf(esc_html__('No pages created with %s yet.', 'metasync'), '<strong>' . esc_html($label) . '</strong>');
1454 1500 echo '</p>';
1455 1501 echo '<p style="text-align: center;">';
1456 1502 echo '<a href="' . esc_url(admin_url('admin.php?page=' . Metasync_Admin::$page_slug)) . '" class="button button-primary">';
1457 1503 echo esc_html__('Get Started', 'metasync');