PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.1
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.1
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-site-health.php +333 -144 2.6.21 → 2.7.1 View file →
@@ -136,52 +136,79 @@
136 136 $rate_limit_log = get_option( 'metasync_otto_rate_limit_log', [] );
137 137 $last_backoff_time = ! empty( $rate_limit_log ) ? max( $rate_limit_log ) : null;
138 138 $last_backoff_value = $last_backoff_time
139 139 ? wp_date( 'Y-m-d H:i:s', $last_backoff_time ) . ' (' . human_time_diff( $last_backoff_time ) . ' ago)'
140 - : __( 'Never' );
140 + : __( 'Never', 'metasync' );
141 141
142 + $otto_job_counts = class_exists( 'Metasync_Otto_Job_Status' ) ? Metasync_Otto_Job_Status::counts() : [];
143 + $otto_job_fields = [];
144 + if ( ! empty( $otto_job_counts ) ) {
145 + $otto_job_fields = [
146 + 'otto_jobs_accepted' => [
147 + 'label' => __( 'Crawl Jobs - Accepted', 'metasync' ),
148 + 'value' => (int) $otto_job_counts['accepted'],
149 + ],
150 + 'otto_jobs_retrying' => [
151 + 'label' => __( 'Crawl Jobs - Retrying', 'metasync' ),
152 + 'value' => (int) $otto_job_counts['retrying'],
153 + ],
154 + 'otto_jobs_completed' => [
155 + 'label' => __( 'Crawl Jobs - Completed', 'metasync' ),
156 + 'value' => (int) $otto_job_counts['completed'],
157 + ],
158 + 'otto_jobs_failed' => [
159 + 'label' => __( 'Crawl Jobs - Failed', 'metasync' ),
160 + 'value' => (int) $otto_job_counts['failed'],
161 + ],
162 + 'otto_jobs_pending' => [
163 + 'label' => __( 'Crawl Jobs - Queued Overflow', 'metasync' ),
164 + 'value' => (int) $otto_job_counts['pending'],
165 + ],
166 + ];
167 + }
168 +
142 169 $info['metasync'] = [
143 170 'label' => $plugin_name,
144 171 'fields' => [
145 172 'version' => [
146 - 'label' => __( 'Plugin Version' ),
173 + 'label' => __( 'Plugin Version', 'metasync' ),
147 174 'value' => METASYNC_VERSION,
148 175 ],
149 176 'queue_pending' => [
150 - 'label' => __( 'Queue - Pending Jobs' ),
177 + 'label' => __( 'Queue - Pending Jobs', 'metasync' ),
151 178 'value' => isset( $queue_stats['metasync_pending'] ) ? (int) $queue_stats['metasync_pending'] : 0,
152 179 ],
153 180 'queue_completed' => [
154 - 'label' => __( 'Queue - Completed Jobs' ),
181 + 'label' => __( 'Queue - Completed Jobs', 'metasync' ),
155 182 'value' => $completed_count,
156 183 ],
157 184 'queue_failed_24h' => [
158 - 'label' => __( 'Queue - Failed Jobs (last 24h)' ),
185 + 'label' => __( 'Queue - Failed Jobs (last 24h)', 'metasync' ),
159 186 'value' => isset( $failed_stats['count'] ) ? (int) $failed_stats['count'] : 0,
160 187 ],
161 - ] + $performance_fields + [
188 + ] + $performance_fields + $otto_job_fields + [
162 189 'last_api_backoff' => [
163 - 'label' => __( 'Last API Backoff Event' ),
190 + 'label' => __( 'Last API Backoff Event', 'metasync' ),
164 191 'value' => $last_backoff_value,
165 192 ],
166 193 'memory_limit' => [
167 - 'label' => __( 'Memory - PHP Limit' ),
194 + 'label' => __( 'Memory - PHP Limit', 'metasync' ),
168 195 'value' => $memory_stats['limit_formatted'],
169 196 ],
170 197 'memory_current' => [
171 - 'label' => __( 'Memory - Current Usage' ),
198 + 'label' => __( 'Memory - Current Usage', 'metasync' ),
172 199 'value' => $memory_stats['current_formatted'],
173 200 ],
174 201 'memory_peak' => [
175 - 'label' => __( 'Memory - Peak Usage' ),
202 + 'label' => __( 'Memory - Peak Usage', 'metasync' ),
176 203 'value' => $memory_stats['peak_formatted'],
177 204 ],
178 205 'memory_percentage' => [
179 - 'label' => __( 'Memory - Usage Percentage' ),
180 - 'value' => $unlimited ? __( 'N/A (unlimited)' ) : $memory_stats['percentage'] . '%',
206 + 'label' => __( 'Memory - Usage Percentage', 'metasync' ),
207 + 'value' => $unlimited ? __( 'N/A (unlimited)', 'metasync' ) : $memory_stats['percentage'] . '%',
181 208 ],
182 209 'memory_available' => [
183 - 'label' => __( 'Memory - Available' ),
210 + 'label' => __( 'Memory - Available', 'metasync' ),
184 211 'value' => $memory_stats['available_formatted'],
185 212 ],
186 213 ],
187 214 ];
@@ -201,36 +228,45 @@
201 228 {
202 229 add_filter('site_status_tests', function ($tests) {
203 230 // WordPress Cron Queue Health Check
204 231 $tests['direct']['metasync_queue_size'] = [
205 - 'label' => __('WordPress Cron Queue Health'),
232 + 'label' => __('WordPress Cron Queue Health', 'metasync'),
206 233 'test' => [$this, 'queue_size_check']
207 234 ];
208 235
209 236 // Memory Usage Health Check
210 237 $tests['direct']['metasync_memory_usage'] = [
211 - 'label' => __('PHP Memory Usage'),
238 + 'label' => __('PHP Memory Usage', 'metasync'),
212 239 'test' => [$this, 'memory_usage_check']
213 240 ];
214 241
215 242 // OTTO API Rate Limit Health Check
216 243 $tests['direct']['metasync_otto_rate_limit'] = [
217 - 'label' => sprintf(__('%s OTTO API Rate Limit'), Metasync::get_effective_plugin_name()),
244 + /* translators: %s: effective plugin name (whitelabel-aware). */
245 + 'label' => sprintf(__('%s OTTO API Rate Limit', 'metasync'), Metasync::get_effective_plugin_name()),
218 246 'test' => [$this, 'otto_rate_limit_check']
219 247 ];
220 248
221 249 // Debug Mode Health Check
222 250 $tests['direct']['metasync_debug_mode'] = [
223 - 'label' => __('WordPress Debug Mode'),
251 + 'label' => __('WordPress Debug Mode', 'metasync'),
224 252 'test' => [$this, 'debug_mode_check']
225 253 ];
226 254
227 255 // Failed Actions Health Check
228 256 $tests['direct']['metasync_failed_actions'] = [
229 - 'label' => sprintf(__('%s Failed Actions'), Metasync::get_effective_plugin_name()),
257 + /* translators: %s: effective plugin name (whitelabel-aware). */
258 + 'label' => sprintf(__('%s Failed Actions', 'metasync'), Metasync::get_effective_plugin_name()),
230 259 'test' => [$this, 'failed_actions_check']
231 260 ];
232 261
262 + // OTTO Crawl Job Reliability Health Check
263 + $tests['direct']['metasync_otto_job_reliability'] = [
264 + /* translators: %s: OTTO name (whitelabel). */
265 + 'label' => sprintf(__('%s Crawl Job Reliability', 'metasync'), Metasync::get_whitelabel_otto_name()),
266 + 'test' => [$this, 'otto_job_reliability_check']
267 + ];
268 +
233 269 return $tests;
234 270 });
235 271 }
236 272
@@ -247,9 +283,9 @@
247 283 private function get_cron_option_size()
248 284 {
249 285 global $wpdb;
250 286
251 - $size = $wpdb->get_var(
287 + $size = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Site Health diagnostic — live on-demand size probe in wp-admin; caching would serve stale sizes
252 288 $wpdb->prepare(
253 289 "SELECT LENGTH(option_value) FROM {$wpdb->options} WHERE option_name = %s",
254 290 'cron'
255 291 )
@@ -361,9 +397,9 @@
361 397
362 398 // Handle excessive size error
363 399 if (isset($stats['error']) && $stats['error'] === 'excessive_size') {
364 400 return [
365 - 'label' => __('WordPress cron queue is excessively large'),
401 + 'label' => __('WordPress cron queue is excessively large', 'metasync'),
366 402 'status' => 'critical',
367 403 'badge' => [
368 404 'label' => Metasync::get_effective_plugin_name(),
369 405 'color' => 'red'
@@ -368,10 +404,11 @@
368 404 'label' => Metasync::get_effective_plugin_name(),
369 405 'color' => 'red'
370 406 ],
371 407 'description' => sprintf(
372 - '<p>' . __('The WordPress cron data is excessively large (%s). This can cause severe performance issues including slow page loads, high memory usage, and potential timeouts.') . '</p>' .
373 - '<p>' . __('This typically occurs when cron jobs accumulate from deactivated plugins or failed operations.') . '</p>',
408 + /* translators: %s: formatted size of the cron data. */
409 + '<p>' . __('The WordPress cron data is excessively large (%s). This can cause severe performance issues including slow page loads, high memory usage, and potential timeouts.', 'metasync') . '</p>' .
410 + '<p>' . __('This typically occurs when cron jobs accumulate from deactivated plugins or failed operations.', 'metasync') . '</p>',
374 411 size_format($stats['size'])
375 412 ),
376 413 'actions' => sprintf(
377 414 '<p><strong>%s</strong></p>' .
@@ -379,12 +416,12 @@
379 416 '<li>%s</li>' .
380 417 '<li>%s</li>' .
381 418 '<li>%s</li>' .
382 419 '</ul>',
383 - __('Recommended Actions:'),
384 - __('Use WP-CLI to list and clean up old cron jobs: <code>wp cron event list</code>'),
385 - __('Install a cron management plugin to identify and remove orphaned jobs'),
386 - __('Consider clearing the cron option and letting WordPress rebuild it from active plugins')
420 + __('Recommended Actions:', 'metasync'),
421 + __('Use WP-CLI to list and clean up old cron jobs: <code>wp cron event list</code>', 'metasync'),
422 + __('Install a cron management plugin to identify and remove orphaned jobs', 'metasync'),
423 + __('Consider clearing the cron option and letting WordPress rebuild it from active plugins', 'metasync')
387 424 ),
388 425 'test' => 'metasync_queue_size'
389 426 ];
390 427 }
@@ -397,12 +434,12 @@
397 434 // Determine status based on TOTAL system-wide ONE-TIME jobs from ALL sources
398 435 // This includes WordPress core, all plugins, themes, and custom code
399 436 if ($one_time_count <= self::PENDING_JOBS_THRESHOLD) {
400 437 $status = 'good';
401 - $label = __('WordPress cron queue is healthy');
438 + $label = __('WordPress cron queue is healthy', 'metasync');
402 439 } else {
403 440 $status = 'critical';
404 - $label = __('WordPress cron queue has high pending job count');
441 + $label = __('WordPress cron queue has high pending job count', 'metasync');
405 442 }
406 443
407 444 // Build detailed description showing system-wide statistics
408 445 $description = sprintf(
@@ -411,14 +448,16 @@
411 448 '<li>%s <strong>%s</strong></li>' .
412 449 '<li>%s %d</li>' .
413 450 '<li>%s %d</li>' .
414 451 '</ul>',
415 - __('WordPress Cron Queue Statistics (All Sources):'),
416 - __('Total pending jobs (one-time events):'),
417 - $truncated ? sprintf(__('%d+'), $one_time_count) : number_format($one_time_count),
418 - __('Total scheduled events (recurring):'),
452 + __('WordPress Cron Queue Statistics (All Sources):', 'metasync'),
453 + __('Total pending jobs (one-time events):', 'metasync'),
454 + /* translators: %d: number of pending one-time cron events. */
455 + $truncated ? sprintf(__('%d+', 'metasync'), $one_time_count) : number_format($one_time_count),
456 + __('Total scheduled events (recurring):', 'metasync'),
419 457 $recurring_count,
420 - sprintf(__('%s pending jobs:'), Metasync::get_effective_plugin_name()),
458 + /* translators: %s: effective plugin name (whitelabel-aware). */
459 + sprintf(__('%s pending jobs:', 'metasync'), Metasync::get_effective_plugin_name()),
421 460 $metasync_count
422 461 );
423 462
424 463 // Add context about what these numbers mean
@@ -423,13 +462,17 @@
423 462
424 463 // Add context about what these numbers mean
425 464 $description .= sprintf(
426 465 '<p><em>%s</em></p>',
427 - __('Pending jobs are one-time tasks awaiting execution. Recurring events are permanent schedules that run automatically.')
466 + __('Pending jobs are one-time tasks awaiting execution. Recurring events are permanent schedules that run automatically.', 'metasync')
428 467 );
429 468
430 - // If status is not good, provide actionable recommendations
431 - if ($status === 'recommended') {
469 + // If status is not good, provide actionable recommendations. The
470 + // status is only ever 'good' or 'critical' here, so the previous
471 + // '=== recommended' comparison made this entire guidance block dead
472 + // code — exactly when the queue went critical, operators got no
473 + // diagnostics at all.
474 + if ($status !== 'good') {
432 475 $description .= sprintf(
433 476 '<p><strong>%s</strong></p>' .
434 477 '<ul>' .
435 478 '<li>%s</li>' .
@@ -435,12 +478,12 @@
435 478 '<li>%s</li>' .
436 479 '<li>%s</li>' .
437 480 '<li>%s</li>' .
438 481 '</ul>',
439 - __('A high number of pending jobs may indicate:'),
440 - __('WordPress cron is not running properly (check if WP-Cron is disabled or blocked)'),
441 - __('High volume of scheduled tasks from plugins creating jobs faster than they can be processed'),
442 - __('Slow server performance or resource constraints preventing timely job execution')
482 + __('A high number of pending jobs may indicate:', 'metasync'),
483 + __('WordPress cron is not running properly (check if WP-Cron is disabled or blocked)', 'metasync'),
484 + __('High volume of scheduled tasks from plugins creating jobs faster than they can be processed', 'metasync'),
485 + __('Slow server performance or resource constraints preventing timely job execution', 'metasync')
443 486 );
444 487
445 488 // If MetaSync is a significant contributor, add specific guidance
446 489 if ($metasync_count > 500) {
@@ -445,11 +488,13 @@
445 488 // If MetaSync is a significant contributor, add specific guidance
446 489 if ($metasync_count > 500) {
447 490 $description .= sprintf(
448 491 '<p><strong>%s</strong> %s</p>',
449 - sprintf(__('%s Contribution:'), Metasync::get_effective_plugin_name()),
492 + /* translators: %s: effective plugin name (whitelabel-aware). */
493 + sprintf(__('%s Contribution:', 'metasync'), Metasync::get_effective_plugin_name()),
450 494 sprintf(
451 - __('%1$s has %2$s pending %3$s SEO processing jobs. This may indicate high crawl volume from %3$s or slow processing. Consider reviewing %3$s crawl settings if this number continues to grow.'),
495 + /* translators: 1: effective plugin name (whitelabel-aware), 2: formatted job count, 3: OTTO name (whitelabel). */
496 + __('%1$s has %2$s pending %3$s SEO processing jobs. This may indicate high crawl volume from %3$s or slow processing. Consider reviewing %3$s crawl settings if this number continues to grow.', 'metasync'),
452 497 Metasync::get_effective_plugin_name(),
453 498 number_format($metasync_count),
454 499 Metasync::get_whitelabel_otto_name()
455 500 )
@@ -463,21 +508,21 @@
463 508 '<li>%s</li>' .
464 509 '<li>%s</li>' .
465 510 '<li>%s</li>' .
466 511 '</ul>',
467 - __('Recommended Actions:'),
468 - __('Verify WordPress cron is functioning: Tools → Site Health → Info → WordPress Constants → DISABLE_WP_CRON should be false'),
469 - __('Check server logs for cron execution errors or timeouts'),
470 - __('Consider using a real system cron instead of WP-Cron for better reliability')
512 + __('Recommended Actions:', 'metasync'),
513 + __('Verify WordPress cron is functioning: Tools → Site Health → Info → WordPress Constants → DISABLE_WP_CRON should be false', 'metasync'),
514 + __('Check server logs for cron execution errors or timeouts', 'metasync'),
515 + __('Consider using a real system cron instead of WP-Cron for better reliability', 'metasync')
471 516 );
472 517
473 518 $description .= sprintf(
474 519 '<p>%s %s</p>',
475 - __( 'Quick link:' ),
520 + __( 'Quick link:', 'metasync' ),
476 521 sprintf(
477 522 '<a href="%s">%s</a>',
478 523 esc_url( admin_url( 'site-health.php?tab=debug' ) ),
479 - esc_html( __( 'Site Health Info (check DISABLE_WP_CRON)' ) )
524 + esc_html( __( 'Site Health Info (check DISABLE_WP_CRON)', 'metasync' ) )
480 525 )
481 526 );
482 527 }
483 528
@@ -542,9 +587,9 @@
542 587 if ($memory_limit === '-1') {
543 588 return [
544 589 'limit' => -1,
545 590 'limit_bytes' => -1,
546 - 'limit_formatted' => __('Unlimited'),
591 + 'limit_formatted' => __('Unlimited', 'metasync'),
547 592 'current' => memory_get_usage(true),
548 593 'current_formatted' => size_format(memory_get_usage(true)),
549 594 'peak' => memory_get_peak_usage(true),
550 595 'peak_formatted' => size_format(memory_get_peak_usage(true)),
@@ -549,9 +594,9 @@
549 594 'peak' => memory_get_peak_usage(true),
550 595 'peak_formatted' => size_format(memory_get_peak_usage(true)),
551 596 'percentage' => 0,
552 597 'available' => -1,
553 - 'available_formatted' => __('Unlimited')
598 + 'available_formatted' => __('Unlimited', 'metasync')
554 599 ];
555 600 }
556 601
557 602 // Convert limit to bytes
@@ -600,9 +645,9 @@
600 645
601 646 // Handle unlimited memory
602 647 if ($stats['limit_bytes'] === -1) {
603 648 return [
604 - 'label' => __('PHP memory limit is unlimited'),
649 + 'label' => __('PHP memory limit is unlimited', 'metasync'),
605 650 'status' => 'good',
606 651 'badge' => [
607 652 'label' => Metasync::get_effective_plugin_name(),
608 653 'color' => 'green'
@@ -613,13 +658,13 @@
613 658 '<ul>' .
614 659 '<li>%s %s</li>' .
615 660 '<li>%s %s</li>' .
616 661 '</ul>',
617 - __('PHP memory limit is set to unlimited. While this prevents memory errors, it may allow poorly optimized code to consume excessive server resources.'),
618 - __('Current Memory Usage:'),
619 - __('Current usage:'),
662 + __('PHP memory limit is set to unlimited. While this prevents memory errors, it may allow poorly optimized code to consume excessive server resources.', 'metasync'),
663 + __('Current Memory Usage:', 'metasync'),
664 + __('Current usage:', 'metasync'),
620 665 $stats['current_formatted'],
621 - __('Peak usage:'),
666 + __('Peak usage:', 'metasync'),
622 667 $stats['peak_formatted']
623 668 ),
624 669 'test' => 'metasync_memory_usage'
625 670 ];
@@ -629,12 +674,12 @@
629 674
630 675 // Determine status based on memory usage percentage
631 676 if ($percentage > self::MEMORY_CRITICAL_THRESHOLD) {
632 677 $status = 'critical';
633 - $label = __('PHP memory usage is critically high');
678 + $label = __('PHP memory usage is critically high', 'metasync');
634 679 } else {
635 680 $status = 'good';
636 - $label = __('PHP memory usage is healthy');
681 + $label = __('PHP memory usage is healthy', 'metasync');
637 682 }
638 683
639 684 // Build detailed description
640 685 $description = sprintf(
@@ -645,19 +690,19 @@
645 690 '<li>%s %s</li>' .
646 691 '<li>%s <strong>%.2f%%</strong></li>' .
647 692 '<li>%s %s</li>' .
648 693 '</ul>',
649 - __('PHP Memory Statistics:'),
650 - __('Memory limit:'),
694 + __('PHP Memory Statistics:', 'metasync'),
695 + __('Memory limit:', 'metasync'),
651 696 $stats['limit_formatted'],
652 697 $stats['limit'],
653 - __('Current usage:'),
698 + __('Current usage:', 'metasync'),
654 699 $stats['current_formatted'],
655 - __('Peak usage:'),
700 + __('Peak usage:', 'metasync'),
656 701 $stats['peak_formatted'],
657 - __('Usage percentage:'),
702 + __('Usage percentage:', 'metasync'),
658 703 $percentage,
659 - __('Available memory:'),
704 + __('Available memory:', 'metasync'),
660 705 $stats['available_formatted']
661 706 );
662 707
663 708 // Add context about memory usage
@@ -663,9 +708,9 @@
663 708 // Add context about memory usage
664 709 if ($status === 'good') {
665 710 $description .= sprintf(
666 711 '<p><em>%s</em></p>',
667 - __('Memory usage is within acceptable limits. The site has sufficient memory available for normal operations.')
712 + __('Memory usage is within acceptable limits. The site has sufficient memory available for normal operations.', 'metasync')
668 713 );
669 714 } else {
670 715 // Critical status - provide actionable recommendations
671 716 $description .= sprintf(
@@ -676,14 +721,14 @@
676 721 '<li>%s</li>' .
677 722 '<li>%s</li>' .
678 723 '<li>%s</li>' .
679 724 '</ul>',
680 - __('Critical Memory Usage Detected:'),
681 - __('PHP memory usage is critically high (>90%). This may cause:'),
682 - __('Fatal errors: "Allowed memory size exhausted"'),
683 - __('Failed page loads and white screens'),
684 - __('Incomplete plugin/theme operations'),
685 - __('Performance degradation and slow response times')
725 + __('Critical Memory Usage Detected:', 'metasync'),
726 + __('PHP memory usage is critically high (>90%). This may cause:', 'metasync'),
727 + __('Fatal errors: "Allowed memory size exhausted"', 'metasync'),
728 + __('Failed page loads and white screens', 'metasync'),
729 + __('Incomplete plugin/theme operations', 'metasync'),
730 + __('Performance degradation and slow response times', 'metasync')
686 731 );
687 732
688 733 $description .= sprintf(
689 734 '<p><strong>%s</strong></p>' .
@@ -693,17 +738,18 @@
693 738 '<li>%s</li>' .
694 739 '<li>%s</li>' .
695 740 '<li>%s</li>' .
696 741 '</ul>',
697 - __('Recommended Actions:'),
742 + __('Recommended Actions:', 'metasync'),
698 743 sprintf(
699 - __('Increase PHP memory limit in wp-config.php: <code>define(\'WP_MEMORY_LIMIT\', \'256M\');</code> (current: %s)'),
744 + /* translators: %s: current PHP memory limit. */
745 + __('Increase PHP memory limit in wp-config.php: <code>define(\'WP_MEMORY_LIMIT\', \'256M\');</code> (current: %s)', 'metasync'),
700 746 $stats['limit']
701 747 ),
702 - __('Contact your hosting provider to increase server memory limits'),
703 - __('Deactivate unnecessary plugins that consume excessive memory'),
704 - __('Optimize images and reduce media library size'),
705 - __('Consider upgrading to a hosting plan with more resources')
748 + __('Contact your hosting provider to increase server memory limits', 'metasync'),
749 + __('Deactivate unnecessary plugins that consume excessive memory', 'metasync'),
750 + __('Optimize images and reduce media library size', 'metasync'),
751 + __('Consider upgrading to a hosting plan with more resources', 'metasync')
706 752 );
707 753
708 754 // Add warning if peak usage is also high
709 755 if ($stats['peak'] > ($stats['limit_bytes'] * 0.95)) {
@@ -708,11 +754,12 @@
708 754 // Add warning if peak usage is also high
709 755 if ($stats['peak'] > ($stats['limit_bytes'] * 0.95)) {
710 756 $description .= sprintf(
711 757 '<p><strong>%s</strong> %s</p>',
712 - __('Warning:'),
758 + __('Warning:', 'metasync'),
713 759 sprintf(
714 - __('Peak memory usage (%s) is very close to the limit. Memory errors may occur during high-traffic periods or resource-intensive operations.'),
760 + /* translators: %s: formatted peak memory usage. */
761 + __('Peak memory usage (%s) is very close to the limit. Memory errors may occur during high-traffic periods or resource-intensive operations.', 'metasync'),
715 762 $stats['peak_formatted']
716 763 )
717 764 );
718 765 }
@@ -718,13 +765,13 @@
718 765 }
719 766
720 767 $description .= sprintf(
721 768 '<p>%s %s</p>',
722 - __( 'Quick link:' ),
769 + __( 'Quick link:', 'metasync' ),
723 770 sprintf(
724 771 '<a href="%s">%s</a>',
725 772 esc_url( admin_url( 'plugins.php' ) ),
726 - esc_html( __( 'Manage Plugins (deactivate memory-heavy plugins)' ) )
773 + esc_html( __( 'Manage Plugins (deactivate memory-heavy plugins)', 'metasync' ) )
727 774 )
728 775 );
729 776 }
730 777
@@ -785,17 +832,19 @@
785 832
786 833 // Format the last hit timestamp if available
787 834 $last_hit_text = $last_hit
788 835 ? sprintf(
789 - __('Last occurrence: %s'),
836 + /* translators: %s: date and time of the last occurrence. */
837 + __('Last occurrence: %s', 'metasync'),
790 838 date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $last_hit)
791 839 )
792 - : __('No rate limit hits recorded.');
840 + : __('No rate limit hits recorded.', 'metasync');
793 841
794 842 if ($hits >= self::RATE_LIMIT_CRITICAL_THRESHOLD) {
795 843 $status = 'critical';
796 844 $label = sprintf(
797 - __('%s OTTO API is being rate limited frequently (%d times in 24h)'),
845 + /* translators: 1: effective plugin name (whitelabel-aware), 2: number of rate limit hits. */
846 + __('%1$s OTTO API is being rate limited frequently (%2$d times in 24h)', 'metasync'),
798 847 Metasync::get_effective_plugin_name(),
799 848 $hits
800 849 );
801 850 } elseif ($hits >= self::RATE_LIMIT_WARNING_THRESHOLD) {
@@ -800,15 +849,17 @@
800 849 );
801 850 } elseif ($hits >= self::RATE_LIMIT_WARNING_THRESHOLD) {
802 851 $status = 'recommended';
803 852 $label = sprintf(
804 - __('%s OTTO API rate limit hit %d times in the last 24 hours'),
853 + /* translators: 1: effective plugin name (whitelabel-aware), 2: number of rate limit hits. */
854 + __('%1$s OTTO API rate limit hit %2$d times in the last 24 hours', 'metasync'),
805 855 Metasync::get_effective_plugin_name(),
806 856 $hits
807 857 );
808 858 } else {
809 859 $status = 'good';
810 - $label = sprintf(__('%s OTTO API rate limits are within normal range'), Metasync::get_effective_plugin_name());
860 + /* translators: %s: effective plugin name (whitelabel-aware). */
861 + $label = sprintf(__('%s OTTO API rate limits are within normal range', 'metasync'), Metasync::get_effective_plugin_name());
811 862 }
812 863
813 864 // Build description
814 865 $description = sprintf(
@@ -816,10 +867,11 @@
816 867 '<ul>' .
817 868 '<li>%s <strong>%d</strong></li>' .
818 869 '<li>%s</li>' .
819 870 '</ul>',
820 - sprintf(__('%s API Rate Limit Statistics (last 24 hours):'), Metasync::get_whitelabel_otto_name()),
821 - __('HTTP 429 responses received:'),
871 + /* translators: %s: OTTO name (whitelabel). */
872 + sprintf(__('%s API Rate Limit Statistics (last 24 hours):', 'metasync'), Metasync::get_whitelabel_otto_name()),
873 + __('HTTP 429 responses received:', 'metasync'),
822 874 $hits,
823 875 $last_hit_text
824 876 );
825 877
@@ -825,9 +877,10 @@
825 877
826 878 if ($status === 'good') {
827 879 $description .= sprintf(
828 880 '<p><em>%s</em></p>',
829 - sprintf(__('The %s API is responding normally. No action required.'), Metasync::get_whitelabel_otto_name())
881 + /* translators: %s: OTTO name (whitelabel). */
882 + sprintf(__('The %s API is responding normally. No action required.', 'metasync'), Metasync::get_whitelabel_otto_name())
830 883 );
831 884 } else {
832 885 $description .= sprintf(
833 886 '<p><strong>%s</strong></p>' .
@@ -836,19 +889,23 @@
836 889 '<li>%s</li>' .
837 890 '<li>%s</li>' .
838 891 '<li>%s</li>' .
839 892 '</ul>',
840 - sprintf(__('Frequent rate limiting can degrade %s SEO data delivery on your site.'), Metasync::get_whitelabel_otto_name()),
841 - __('Recommended Actions:'),
842 - sprintf(__('Review your %1$s crawl frequency settings in the %2$s dashboard to reduce request volume.'), Metasync::get_whitelabel_otto_name(), Metasync::get_effective_plugin_name()),
843 - sprintf(__('Check if multiple server processes are triggering %s jobs simultaneously.'), Metasync::get_whitelabel_otto_name()),
844 - sprintf(__('Contact %s support if rate limiting persists despite low crawl volume.'), Metasync::get_effective_plugin_name())
893 + /* translators: %s: OTTO name (whitelabel). */
894 + sprintf(__('Frequent rate limiting can degrade %s SEO data delivery on your site.', 'metasync'), Metasync::get_whitelabel_otto_name()),
895 + __('Recommended Actions:', 'metasync'),
896 + /* translators: 1: OTTO name (whitelabel), 2: effective plugin name (whitelabel-aware). */
897 + sprintf(__('Review your %1$s crawl frequency settings in the %2$s dashboard to reduce request volume.', 'metasync'), Metasync::get_whitelabel_otto_name(), Metasync::get_effective_plugin_name()),
898 + /* translators: %s: OTTO name (whitelabel). */
899 + sprintf(__('Check if multiple server processes are triggering %s jobs simultaneously.', 'metasync'), Metasync::get_whitelabel_otto_name()),
900 + /* translators: %s: effective plugin name (whitelabel-aware). */
901 + sprintf(__('Contact %s support if rate limiting persists despite low crawl volume.', 'metasync'), Metasync::get_effective_plugin_name())
845 902 );
846 903
847 904 $description .= sprintf(
848 905 '<p>%s %s</p>',
849 - __( 'Quick link:' ),
850 - $this->get_admin_link( '&tab=general', __( 'Review API Settings' ) )
906 + __( 'Quick link:', 'metasync' ),
907 + $this->get_admin_link( '&tab=general', __( 'Review API Settings', 'metasync' ) )
851 908 );
852 909 }
853 910
854 911 return [
@@ -913,9 +970,9 @@
913 970
914 971 // Good state: no debug constants active
915 972 if ( ! $any_debug_on ) {
916 973 return [
917 - 'label' => __('Debug mode is disabled'),
974 + 'label' => __('Debug mode is disabled', 'metasync'),
918 975 'status' => 'good',
919 976 'badge' => [
920 977 'label' => Metasync::get_effective_plugin_name(),
921 978 'color' => 'green',
@@ -921,9 +978,9 @@
921 978 'color' => 'green',
922 979 ],
923 980 'description' => sprintf(
924 981 '<p>%s</p>',
925 - __('WordPress debug mode is not active. This is the recommended configuration for production sites.')
982 + __('WordPress debug mode is not active. This is the recommended configuration for production sites.', 'metasync')
926 983 ),
927 984 'test' => 'metasync_debug_mode',
928 985 ];
929 986 }
@@ -929,11 +986,11 @@
929 986 }
930 987
931 988 // Recommended state: one or more debug constants are active
932 989 if ( $stats['wp_debug_display'] ) {
933 - $label = __('Debug mode is enabled with error display active');
990 + $label = __('Debug mode is enabled with error display active', 'metasync');
934 991 } else {
935 - $label = __('Debug mode is enabled on this site');
992 + $label = __('Debug mode is enabled on this site', 'metasync');
936 993 }
937 994
938 995 // Status table showing each flag's current state
939 996 $description = sprintf(
@@ -942,15 +999,15 @@
942 999 '<li>%s <strong>%s</strong></li>' .
943 1000 '<li>%s <strong>%s</strong></li>' .
944 1001 '<li>%s <strong>%s</strong></li>' .
945 1002 '</ul>',
946 - __('Active Debug Configuration:'),
947 - __('WP_DEBUG:'),
948 - $stats['wp_debug'] ? __('Enabled') : __('Disabled'),
949 - __('WP_DEBUG_LOG:'),
950 - $stats['wp_debug_log'] ? __('Enabled') : __('Disabled'),
951 - __('WP_DEBUG_DISPLAY:'),
952 - $stats['wp_debug_display'] ? __('Enabled') : __('Disabled')
1003 + __('Active Debug Configuration:', 'metasync'),
1004 + __('WP_DEBUG:', 'metasync'),
1005 + $stats['wp_debug'] ? __('Enabled', 'metasync') : __('Disabled', 'metasync'),
1006 + __('WP_DEBUG_LOG:', 'metasync'),
1007 + $stats['wp_debug_log'] ? __('Enabled', 'metasync') : __('Disabled', 'metasync'),
1008 + __('WP_DEBUG_DISPLAY:', 'metasync'),
1009 + $stats['wp_debug_display'] ? __('Enabled', 'metasync') : __('Disabled', 'metasync')
953 1010 );
954 1011
955 1012 // Call out METASYNC_DEBUG if it is separately active
956 1013 if ( $stats['metasync_debug'] ) {
@@ -955,9 +1012,9 @@
955 1012 // Call out METASYNC_DEBUG if it is separately active
956 1013 if ( $stats['metasync_debug'] ) {
957 1014 $description .= sprintf(
958 1015 '<p>%s</p>',
959 - __('The <code>METASYNC_DEBUG</code> constant is also active. This enables additional MetaSync diagnostic output.')
1016 + __('The <code>METASYNC_DEBUG</code> constant is also active. This enables additional MetaSync diagnostic output.', 'metasync')
960 1017 );
961 1018 }
962 1019
963 1020 // Escalated warning when WP_DEBUG_DISPLAY is on (errors visible to site visitors)
@@ -963,16 +1020,16 @@
963 1020 // Escalated warning when WP_DEBUG_DISPLAY is on (errors visible to site visitors)
964 1021 if ( $stats['wp_debug_display'] ) {
965 1022 $description .= sprintf(
966 1023 '<p><strong>%s</strong> %s</p>',
967 - __('Warning:'),
968 - __('WP_DEBUG_DISPLAY is active. PHP errors and notices may be visible to your site visitors, potentially exposing file paths, database details, and plugin internals. This should be disabled on any publicly accessible site.')
1024 + __('Warning:', 'metasync'),
1025 + __('WP_DEBUG_DISPLAY is active. PHP errors and notices may be visible to your site visitors, potentially exposing file paths, database details, and plugin internals. This should be disabled on any publicly accessible site.', 'metasync')
969 1026 );
970 1027 }
971 1028
972 1029 $description .= sprintf(
973 1030 '<p><em>%s</em></p>',
974 - __('Debug mode is intended for development environments. On a production site, disabling it prevents information leakage and removes the performance overhead of error collection.')
1031 + __('Debug mode is intended for development environments. On a production site, disabling it prevents information leakage and removes the performance overhead of error collection.', 'metasync')
975 1032 );
976 1033
977 1034 $description .= sprintf(
978 1035 '<p><strong>%s</strong></p>' .
@@ -979,17 +1036,17 @@
979 1036 '<ul>' .
980 1037 '<li>%s</li>' .
981 1038 '<li>%s</li>' .
982 1039 '</ul>',
983 - __('Recommended Actions:'),
984 - __('Set <code>define(\'WP_DEBUG\', false);</code> in your wp-config.php if this is a live production site.'),
985 - __('If error logging is needed in production, avoid WP_DEBUG_LOG — it writes to <code>wp-content/debug.log</code> which is publicly accessible via browser and can expose file paths, database details, and API keys. Use a dedicated logging solution that writes outside the webroot instead.')
1040 + __('Recommended Actions:', 'metasync'),
1041 + __('Set <code>define(\'WP_DEBUG\', false);</code> in your wp-config.php if this is a live production site.', 'metasync'),
1042 + __('If error logging is needed in production, avoid WP_DEBUG_LOG — it writes to <code>wp-content/debug.log</code> which is publicly accessible via browser and can expose file paths, database details, and API keys. Use a dedicated logging solution that writes outside the webroot instead.', 'metasync')
986 1043 );
987 1044
988 1045 $description .= sprintf(
989 1046 '<p>%s %s</p>',
990 - __( 'Quick link:' ),
991 - $this->get_admin_link( '&tab=advanced', __( 'Advanced Settings' ) )
1047 + __( 'Quick link:', 'metasync' ),
1048 + $this->get_admin_link( '&tab=advanced', __( 'Advanced Settings', 'metasync' ) )
992 1049 );
993 1050
994 1051 return [
995 1052 'label' => $label,
@@ -1017,15 +1074,15 @@
1017 1074 {
1018 1075 // WP-Cron mode
1019 1076 $wp_cron_disabled = defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON;
1020 1077 $wp_cron_mode = $wp_cron_disabled
1021 - ? __( 'System Cron (DISABLE_WP_CRON = true)' )
1022 - : __( 'WP-Cron (default)' );
1078 + ? __( 'System Cron (DISABLE_WP_CRON = true)', 'metasync' )
1079 + : __( 'WP-Cron (default)', 'metasync' );
1023 1080
1024 1081 // PHP max execution time
1025 1082 $max_exec_raw = ini_get( 'max_execution_time' );
1026 1083 $max_exec = $max_exec_raw === '0' || $max_exec_raw === 0
1027 - ? __( 'Unlimited (0)' )
1084 + ? __( 'Unlimited (0)', 'metasync' )
1028 1085 : (int) $max_exec_raw . 's';
1029 1086
1030 1087 // Next scheduled transient cleanup
1031 1088 $next_cleanup_ts = wp_next_scheduled( 'metasync_cleanup_transients' );
@@ -1030,9 +1087,9 @@
1030 1087 // Next scheduled transient cleanup
1031 1088 $next_cleanup_ts = wp_next_scheduled( 'metasync_cleanup_transients' );
1032 1089 $next_cleanup_val = $next_cleanup_ts
1033 1090 ? wp_date( 'Y-m-d H:i:s', $next_cleanup_ts ) . ' (in ' . human_time_diff( $next_cleanup_ts ) . ')'
1034 - : __( 'Not scheduled' );
1091 + : __( 'Not scheduled', 'metasync' );
1035 1092
1036 1093 // Telemetry flush interval label
1037 1094 $flush_interval = Metasync_Telemetry_Config::QUEUE_FLUSH_INTERVAL;
1038 1095 $schedules = wp_get_schedules();
@@ -1041,49 +1098,52 @@
1041 1098 : $flush_interval;
1042 1099
1043 1100 // OTTO suggestions cache TTL (stored in seconds, display as minutes)
1044 1101 $otto_cache_ttl_sec = Metasync_Otto_Config::get_otto_cache_ttl_seconds();
1045 - $otto_cache_ttl_val = ( $otto_cache_ttl_sec / MINUTE_IN_SECONDS ) . ' ' . __( 'minutes' );
1102 + $otto_cache_ttl_val = ( $otto_cache_ttl_sec / MINUTE_IN_SECONDS ) . ' ' . __( 'minutes', 'metasync' );
1046 1103
1047 1104 return [
1048 1105 'perf_wp_cron' => [
1049 - 'label' => __( 'Performance - WP-Cron Mode' ),
1106 + 'label' => __( 'Performance - WP-Cron Mode', 'metasync' ),
1050 1107 'value' => $wp_cron_mode,
1051 1108 ],
1052 1109 'perf_php_max_exec' => [
1053 - 'label' => __( 'Performance - PHP Max Execution Time' ),
1110 + 'label' => __( 'Performance - PHP Max Execution Time', 'metasync' ),
1054 1111 'value' => $max_exec,
1055 1112 ],
1056 1113 'perf_otto_request_timeout' => [
1057 - 'label' => sprintf(__('Performance - %s API Request Timeout'), Metasync::get_whitelabel_otto_name()),
1114 + /* translators: %s: OTTO name (whitelabel). */
1115 + 'label' => sprintf(__('Performance - %s API Request Timeout', 'metasync'), Metasync::get_whitelabel_otto_name()),
1058 1116 'value' => '30s',
1059 1117 ],
1060 1118 'perf_otto_cache_ttl' => [
1061 - 'label' => sprintf(__('Performance - %s Suggestions Cache TTL'), Metasync::get_whitelabel_otto_name()),
1119 + /* translators: %s: OTTO name (whitelabel). */
1120 + 'label' => sprintf(__('Performance - %s Suggestions Cache TTL', 'metasync'), Metasync::get_whitelabel_otto_name()),
1062 1121 'value' => $otto_cache_ttl_val,
1063 1122 ],
1064 1123 'perf_otto_max_calls' => [
1065 - 'label' => sprintf(__('Performance - %s Max API Calls/Min'), Metasync::get_whitelabel_otto_name()),
1124 + /* translators: %s: OTTO name (whitelabel). */
1125 + 'label' => sprintf(__('Performance - %s Max API Calls/Min', 'metasync'), Metasync::get_whitelabel_otto_name()),
1066 1126 'value' => Metasync_Otto_Transient_Cache::MAX_API_CALLS_PER_MINUTE,
1067 1127 ],
1068 1128 'perf_telemetry_queue_max' => [
1069 - 'label' => __( 'Performance - Telemetry Max Queue Size' ),
1129 + 'label' => __( 'Performance - Telemetry Max Queue Size', 'metasync' ),
1070 1130 'value' => Metasync_Telemetry_Config::MAX_QUEUE_SIZE,
1071 1131 ],
1072 1132 'perf_telemetry_batch' => [
1073 - 'label' => __( 'Performance - Telemetry Batch Size' ),
1133 + 'label' => __( 'Performance - Telemetry Batch Size', 'metasync' ),
1074 1134 'value' => Metasync_Telemetry_Config::QUEUE_BATCH_SIZE,
1075 1135 ],
1076 1136 'perf_telemetry_flush' => [
1077 - 'label' => __( 'Performance - Telemetry Queue Flush Interval' ),
1137 + 'label' => __( 'Performance - Telemetry Queue Flush Interval', 'metasync' ),
1078 1138 'value' => $flush_interval_label,
1079 1139 ],
1080 1140 'perf_api_max_retries' => [
1081 - 'label' => __( 'Performance - API Max Retry Attempts' ),
1141 + 'label' => __( 'Performance - API Max Retry Attempts', 'metasync' ),
1082 1142 'value' => Metasync_Telemetry_Config::MAX_RETRY_ATTEMPTS,
1083 1143 ],
1084 1144 'perf_next_cleanup' => [
1085 - 'label' => __( 'Performance - Next Transient Cleanup' ),
1145 + 'label' => __( 'Performance - Next Transient Cleanup', 'metasync' ),
1086 1146 'value' => $next_cleanup_val,
1087 1147 ],
1088 1148 ];
1089 1149 }
@@ -1132,23 +1192,26 @@
1132 1192 $last_hit = $stats['last_hit'];
1133 1193
1134 1194 $last_hit_text = $last_hit
1135 1195 ? sprintf(
1136 - __( 'Last failure: %s' ),
1196 + /* translators: %s: date and time of the last failure. */
1197 + __( 'Last failure: %s', 'metasync' ),
1137 1198 date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $last_hit )
1138 1199 )
1139 - : __( 'No failed actions recorded in the last 24 hours.' );
1200 + : __( 'No failed actions recorded in the last 24 hours.', 'metasync' );
1140 1201
1141 1202 if ( $count > self::FAILED_ACTIONS_THRESHOLD ) {
1142 1203 $status = 'recommended';
1143 1204 $label = sprintf(
1144 - __( '%s has %d failed SEO processing actions in the last 24 hours' ),
1205 + /* translators: 1: effective plugin name (whitelabel-aware), 2: number of failed actions. */
1206 + __( '%1$s has %2$d failed SEO processing actions in the last 24 hours', 'metasync' ),
1145 1207 Metasync::get_effective_plugin_name(),
1146 1208 $count
1147 1209 );
1148 1210 } else {
1149 1211 $status = 'good';
1150 - $label = sprintf(__('%s SEO action queue is healthy'), Metasync::get_effective_plugin_name());
1212 + /* translators: %s: effective plugin name (whitelabel-aware). */
1213 + $label = sprintf(__('%s SEO action queue is healthy', 'metasync'), Metasync::get_effective_plugin_name());
1151 1214 }
1152 1215
1153 1216 $description = sprintf(
1154 1217 '<p><strong>%s</strong></p>' .
@@ -1155,10 +1218,12 @@
1155 1218 '<ul>' .
1156 1219 '<li>%s <strong>%d</strong></li>' .
1157 1220 '<li>%s</li>' .
1158 1221 '</ul>',
1159 - sprintf(__('%s Failed Actions Statistics (last 24 hours):'), Metasync::get_effective_plugin_name()),
1160 - sprintf(__('Failed %s SEO processing jobs:'), Metasync::get_whitelabel_otto_name()),
1222 + /* translators: %s: effective plugin name (whitelabel-aware). */
1223 + sprintf(__('%s Failed Actions Statistics (last 24 hours):', 'metasync'), Metasync::get_effective_plugin_name()),
1224 + /* translators: %s: OTTO name (whitelabel). */
1225 + sprintf(__('Failed %s SEO processing jobs:', 'metasync'), Metasync::get_whitelabel_otto_name()),
1161 1226 $count,
1162 1227 $last_hit_text
1163 1228 );
1164 1229
@@ -1164,9 +1229,10 @@
1164 1229
1165 1230 if ( $status === 'good' ) {
1166 1231 $description .= sprintf(
1167 1232 '<p><em>%s</em></p>',
1168 - sprintf(__('%s SEO processing jobs are completing successfully. No action required.'), Metasync::get_whitelabel_otto_name())
1233 + /* translators: %s: OTTO name (whitelabel). */
1234 + sprintf(__('%s SEO processing jobs are completing successfully. No action required.', 'metasync'), Metasync::get_whitelabel_otto_name())
1169 1235 );
1170 1236 } else {
1171 1237 $description .= sprintf(
1172 1238 '<p><strong>%s</strong></p>' .
@@ -1174,19 +1240,22 @@
1174 1240 '<li>%s</li>' .
1175 1241 '<li>%s</li>' .
1176 1242 '<li>%s</li>' .
1177 1243 '</ul>',
1178 - __( 'Recommended Actions:' ),
1179 - sprintf(__('Check your %1$s API connection in %1$s → Settings.'), Metasync::get_effective_plugin_name()),
1180 - sprintf(__('Review your PHP error log for %s processing exceptions.'), Metasync::get_whitelabel_otto_name()),
1181 - sprintf(__('Verify your %1$s UUID is correctly configured and the %2$s API is reachable.'), Metasync::get_whitelabel_otto_name(), Metasync::get_effective_plugin_name())
1244 + __( 'Recommended Actions:', 'metasync' ),
1245 + /* translators: 1: effective plugin name (whitelabel-aware). */
1246 + sprintf(__('Check your %1$s API connection in %1$s → Settings.', 'metasync'), Metasync::get_effective_plugin_name()),
1247 + /* translators: %s: OTTO name (whitelabel). */
1248 + sprintf(__('Review your PHP error log for %s processing exceptions.', 'metasync'), Metasync::get_whitelabel_otto_name()),
1249 + /* translators: 1: OTTO name (whitelabel), 2: effective plugin name (whitelabel-aware). */
1250 + sprintf(__('Verify your %1$s UUID is correctly configured and the %2$s API is reachable.', 'metasync'), Metasync::get_whitelabel_otto_name(), Metasync::get_effective_plugin_name())
1182 1251 );
1183 1252
1184 1253 $description .= sprintf(
1185 1254 '<p>%s %s &middot; %s</p>',
1186 - __( 'Quick links:' ),
1187 - $this->get_admin_link( '&tab=general', __( 'Review API Settings' ) ),
1188 - $this->get_admin_link( '-sync-log', __( 'View Changes Log' ) )
1255 + __( 'Quick links:', 'metasync' ),
1256 + $this->get_admin_link( '&tab=general', __( 'Review API Settings', 'metasync' ) ),
1257 + $this->get_admin_link( '-sync-log', __( 'View Changes Log', 'metasync' ) )
1189 1258 );
1190 1259 }
1191 1260
1192 1261 return [
@@ -1198,6 +1267,126 @@
1198 1267 ],
1199 1268 'description' => $description,
1200 1269 'test' => 'metasync_failed_actions',
1201 1270 ];
1271 + }
1272 +
1273 + /**
1274 + * Site Health test for OTTO crawl-notify background job reliability
1275 + *
1276 + * Reads the bounded per-URL status store (Metasync_Otto_Job_Status) that
1277 + * the crawl-notify pipeline writes as its background jobs run, so an
1278 + * administrator can see what actually happened to notified URLs — not just
1279 + * that the webhook was acknowledged.
1280 + *
1281 + * Status levels:
1282 + * - good: no failed URLs and fewer than the threshold stuck retrying
1283 + * - recommended: failed URLs exist or retrying count exceeds the threshold
1284 + *
1285 + * When WP-Cron is disabled (DISABLE_WP_CRON) without a visible system
1286 + * crontab entry the pipeline cannot run at all, so an advisory is always
1287 + * included in that case.
1288 + *
1289 + * @since 1.0.0
1290 + * @return array Site Health test result
1291 + */
1292 + public function otto_job_reliability_check()
1293 + {
1294 + $otto_name = Metasync::get_whitelabel_otto_name();
1295 +
1296 + if ( ! class_exists( 'Metasync_Otto_Job_Status' ) ) {
1297 + return [
1298 + /* translators: %s: OTTO name (whitelabel). */
1299 + 'label' => sprintf( __( '%s crawl job tracking is unavailable', 'metasync' ), $otto_name ),
1300 + 'status' => 'good',
1301 + 'badge' => [
1302 + 'label' => Metasync::get_effective_plugin_name(),
1303 + 'color' => 'green',
1304 + ],
1305 + 'description' => '<p>' . esc_html__( 'The status store class is not loaded; nothing to report.', 'metasync' ) . '</p>',
1306 + 'test' => 'metasync_otto_job_reliability',
1307 + ];
1308 + }
1309 +
1310 + $counts = Metasync_Otto_Job_Status::counts();
1311 +
1312 + $retrying_threshold = 25;
1313 + if ( $counts['failed'] > 0 || $counts['retrying'] > $retrying_threshold ) {
1314 + $status = 'recommended';
1315 + $label = sprintf(
1316 + /* translators: 1: OTTO name (whitelabel), 2: number of failed jobs, 3: number of retrying jobs. */
1317 + __( '%1$s crawl jobs need attention (%2$d failed, %3$d retrying)', 'metasync' ),
1318 + $otto_name,
1319 + $counts['failed'],
1320 + $counts['retrying']
1321 + );
1322 + } else {
1323 + $status = 'good';
1324 + /* translators: %s: OTTO name (whitelabel). */
1325 + $label = sprintf( __( '%s crawl jobs are completing normally', 'metasync' ), $otto_name );
1326 + }
1327 +
1328 + $description = sprintf(
1329 + '<p><strong>%s</strong></p><ul><li>%s <strong>%d</strong></li><li>%s <strong>%d</strong></li><li>%s <strong>%d</strong></li><li>%s <strong>%d</strong></li><li>%s <strong>%d</strong></li></ul>',
1330 + /* translators: %s: OTTO name (whitelabel). */
1331 + sprintf( __( '%s crawl job status (last 48 hours):', 'metasync' ), $otto_name ),
1332 + __( 'Accepted, awaiting run:', 'metasync' ),
1333 + $counts['accepted'],
1334 + __( 'Retrying after transient failures:', 'metasync' ),
1335 + $counts['retrying'],
1336 + __( 'Completed:', 'metasync' ),
1337 + $counts['completed'],
1338 + __( 'Failed (retries exhausted):', 'metasync' ),
1339 + $counts['failed'],
1340 + __( 'Queued past the per-batch cap:', 'metasync' ),
1341 + $counts['pending']
1342 + );
1343 +
1344 + if ( $this->constant_is_truthy( 'DISABLE_WP_CRON' ) ) {
1345 + $description .= sprintf(
1346 + '<p><strong>%s</strong> %s</p>',
1347 + __( 'WP-Cron is disabled on this site.', 'metasync' ),
1348 + sprintf(
1349 + /* translators: %s: OTTO product name */
1350 + __( 'Background jobs only run if a system crontab (or equivalent) calls wp-cron.php. Without it, %s crawl notifications are queued but never processed.', 'metasync' ),
1351 + $otto_name
1352 + )
1353 + );
1354 + }
1355 +
1356 + if ( $status !== 'good' ) {
1357 + $description .= sprintf(
1358 + '<p>%s %s &middot; %s</p>',
1359 + __( 'Quick links:', 'metasync' ),
1360 + $this->get_admin_link( '&tab=general', __( 'Review API Settings', 'metasync' ) ),
1361 + $this->get_admin_link( '-sync-log', __( 'View Changes Log', 'metasync' ) )
1362 + );
1363 + }
1364 +
1365 + return [
1366 + 'label' => $label,
1367 + 'status' => $status,
1368 + 'badge' => [
1369 + 'label' => Metasync::get_effective_plugin_name(),
1370 + 'color' => $status === 'recommended' ? 'orange' : 'green',
1371 + ],
1372 + 'description' => $description,
1373 + 'test' => 'metasync_otto_job_reliability',
1374 + ];
1375 + }
1376 +
1377 + /**
1378 + * Whether a named constant is defined and evaluates truthy.
1379 + *
1380 + * The constant name arrives as data so the value is resolved through
1381 + * constant() at runtime; some constants are also loaded from WP stubs
1382 + * with fixed values, which makes a direct `defined() && CONST` check
1383 + * statically redundant even though the live value varies per site.
1384 + *
1385 + * @since 1.0.0
1386 + * @param string $name Constant name, without the leading backslash.
1387 + * @return bool
1388 + */
1389 + private function constant_is_truthy( $name ) {
1390 + return defined( $name ) && filter_var( constant( $name ), FILTER_VALIDATE_BOOLEAN );
1202 1391 }
1203 1392 }