PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.14.2
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.14.2
2.14.3 2.14.2 2.14.1 2.14.0 2.13.0 2.12.0 2.11.0 2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 All 58 releases
← All changes | includes/seo/email-report-sections/class-top-losing-keywords-section.php +27 -11 2.0.2 → 2.14.2 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Top Losing Keywords Section
3 + * Top Losing Keywords Section — "Queries losing ground".
4 4 *
5 5 * Queries with the largest click loss vs the previous period, computed from
6 6 * the shared current-vs-previous comparison the Data Provider builds from
7 7 * Search Console (query dimension). Ranking is on the click delta, not on
@@ -29,9 +29,9 @@
29 29 return 'top_losing_keywords';
30 30 }
31 31
32 32 public function label(): string {
33 - return __('Top Losing Keywords', 'thinkrank');
33 + return __('Queries losing ground', 'thinkrank');
34 34 }
35 35
36 36 public function default_enabled(): bool {
37 37 return true;
@@ -40,8 +40,12 @@
40 40 public function requires_capability(): ?string {
41 41 return null;
42 42 }
43 43
44 + public function renders_own_heading(): bool {
45 + return true;
46 + }
47 +
44 48 public function collect(array $context): array {
45 49 $shared = $context['shared'] ?? [];
46 50 $comparison = $shared['comparison'] ?? [];
47 51 if (empty($comparison['available']) || empty($comparison['queries'])) {
@@ -55,14 +59,15 @@
55 59 if ($delta >= 0) {
56 60 continue;
57 61 }
58 62 $rows[] = [
59 - 'query' => $entry['query'] ?? '',
63 + 'query' => $entry['query'] ?? '',
60 64 // A keyword that dropped out entirely has no current position.
61 - // Keep it null so the renderer shows a dash instead of "0.0".
62 - 'position' => isset($entry['cur_pos']) ? (float) $entry['cur_pos'] : null,
63 - 'clicks' => (int) $entry['cur_clicks'],
64 - 'change' => $delta,
65 + // Keep it null so the row says so instead of "0.0".
66 + 'position' => isset($entry['cur_pos']) ? (float) $entry['cur_pos'] : null,
67 + 'prev_position' => isset($entry['prev_pos']) ? (float) $entry['prev_pos'] : null,
68 + 'clicks' => (int) $entry['cur_clicks'],
69 + 'change' => $delta,
65 70 ];
66 71 }
67 72
68 73 // Biggest click loss first (most negative).
@@ -72,14 +77,25 @@
72 77 'rows' => array_slice($rows, 0, self::MAX_ROWS),
73 78 ];
74 79 }
75 80
81 + public function has_data(array $payload): bool {
82 + return !empty($payload['rows']);
83 + }
84 +
76 85 public function render(array $payload): string {
77 - return Top_Posts_Renderer::render_keywords($payload['rows'] ?? [], 'loss');
86 + $rows = Top_Posts_Renderer::keyword_rows($payload['rows'] ?? []);
87 + if ($rows === []) {
88 + return '';
89 + }
90 + return Email_Report_Html::heading(
91 + $this->label(),
92 + __('Worth a look: these search terms lost the most clicks vs the previous period', 'thinkrank')
93 + )
94 + . Email_Report_Html::list_rows($rows, 'down')
95 + . Email_Report_Html::link(__('See all queries', 'thinkrank'), Email_Report_Html::admin_link('analytics', 'keywords'));
78 96 }
79 97
80 98 public function fallback_html(): string {
81 - return '<p style="color:#6b7280;font-style:italic;">'
82 - . esc_html__('Search Console data unavailable. Connect Search Console to see top losing keywords.', 'thinkrank')
83 - . '</p>';
99 + return '';
84 100 }
85 101 }