PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.11.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.11.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 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 All 52 releases
← All changes | includes/seo/email-report-sections/class-top-posts-renderer.php +62 -81 1.27.0 → 2.11.0 View file →
@@ -1,11 +1,11 @@
1 1 <?php
2 2 /**
3 3 * Top Posts Renderer
4 4 *
5 - * Shared HTML helper used by Top Winning Posts and Top Losing Posts so
6 - * both render identically. Not a section itself — purely a stateless
7 - * presentation helper.
5 + * Shared row-shaping helper used by the four growing / losing sections so
6 + * pages and queries read the same way in every card. Not a section itself
7 + * — purely a stateless presentation helper on top of Email_Report_Html.
8 8 *
9 9 * @package ThinkRank
10 10 * @subpackage SEO\Email_Report_Sections
11 11 * @since 1.9.0
@@ -21,109 +21,90 @@
21 21
22 22 final class Top_Posts_Renderer {
23 23
24 24 /**
25 - * Render a page table (page, clicks, click change vs previous period).
25 + * Shape page entries into list rows: title (resolved from the URL when
26 + * it is one of this site's posts), the path underneath, a ±N clicks pill.
26 27 *
27 - * @param array $rows Each row: ['url'|'page' => string, 'clicks' => int, 'change' => ?int, 'title' => ?string]
28 - * @param string $variant 'gain' | 'loss' — controls accent color only.
28 + * @param array $rows Each row: ['url' => string, 'clicks' => int, 'change' => int]
29 + * @return array<int,array{title:string,subtitle:string,pill:string,href:string}>
29 30 */
30 - public static function render(array $rows, string $variant = 'gain'): string {
31 - if (empty($rows)) {
32 - return '';
33 - }
34 -
35 - $accent = $variant === 'loss' ? '#dc2626' : '#16a34a';
36 -
37 - $html = '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;">';
38 - $html .= '<tr>'
39 - . self::th(__('Page', 'thinkrank'), 'left')
40 - . self::th(__('Clicks', 'thinkrank'), 'right')
41 - . self::th(__('Change', 'thinkrank'), 'right')
42 - . '</tr>';
43 -
31 + public static function page_rows(array $rows): array {
32 + $out = [];
44 33 foreach ($rows as $row) {
45 34 $url = (string) ($row['url'] ?? $row['page'] ?? '');
35 + if ($url === '') {
36 + continue;
37 + }
46 38 $title = (string) ($row['title'] ?? '');
47 - $clicks = (int) ($row['clicks'] ?? 0);
48 - $display = $title !== '' ? $title : $url;
49 -
50 - $html .= '<tr>'
51 - . '<td style="padding:10px;border-bottom:1px solid #f3f4f6;">'
52 - . ($url !== ''
53 - ? '<a href="' . esc_url($url) . '" style="color:#111827;text-decoration:none;">' . esc_html($display) . '</a>'
54 - : '<span style="color:#111827;">' . esc_html($display) . '</span>')
55 - . '</td>'
56 - . '<td style="padding:10px;border-bottom:1px solid #f3f4f6;text-align:right;color:#374151;font:600 13px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;">'
57 - . esc_html(number_format_i18n($clicks))
58 - . '</td>'
59 - . self::change_cell($row['change'] ?? null, $accent)
60 - . '</tr>';
39 + if ($title === '') {
40 + $title = Email_Report_Html::page_title($url);
41 + }
42 + $path = Email_Report_Html::display_path($url);
43 + $out[] = [
44 + 'title' => $title,
45 + 'subtitle' => $path !== $title ? $path : '',
46 + 'pill' => self::clicks_pill((int) ($row['change'] ?? 0)),
47 + 'href' => $url,
48 + ];
61 49 }
62 -
63 - $html .= '</table>';
64 - return $html;
50 + return $out;
65 51 }
66 52
67 53 /**
68 - * Render a keyword table (query, position, clicks, click change).
54 + * Shape query entries into list rows: the query, its average position
55 + * (and the previous one when it moved), a ±N clicks pill.
69 56 *
70 - * @param array $rows Each row: ['query' => string, 'position' => float, 'clicks' => int, 'change' => ?int]
71 - * @param string $variant 'gain' | 'loss'
57 + * @param array $rows Each row: ['query' => string, 'position' => ?float, 'prev_position' => ?float, 'change' => int]
58 + * @return array<int,array{title:string,subtitle:string,pill:string}>
72 59 */
73 - public static function render_keywords(array $rows, string $variant = 'gain'): string {
74 - if (empty($rows)) {
75 - return '';
76 - }
77 -
78 - $accent = $variant === 'loss' ? '#dc2626' : '#16a34a';
79 -
80 - $html = '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;">';
81 - $html .= '<tr>'
82 - . self::th(__('Keyword', 'thinkrank'), 'left')
83 - . self::th(__('Position', 'thinkrank'), 'right')
84 - . self::th(__('Clicks', 'thinkrank'), 'right')
85 - . self::th(__('Change', 'thinkrank'), 'right')
86 - . '</tr>';
87 -
60 + public static function keyword_rows(array $rows): array {
61 + $out = [];
88 62 foreach ($rows as $row) {
89 63 $query = (string) ($row['query'] ?? ($row['keys'][0] ?? ''));
90 - $position = (float) ($row['position'] ?? 0);
91 - $clicks = (int) ($row['clicks'] ?? 0);
92 64 if ($query === '') {
93 65 continue;
94 66 }
95 - $html .= '<tr>'
96 - . '<td style="padding:10px;border-bottom:1px solid #f3f4f6;color:#111827;">' . esc_html($query) . '</td>'
97 - . '<td style="padding:10px;border-bottom:1px solid #f3f4f6;text-align:right;color:#374151;font:600 13px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;">' . esc_html(number_format_i18n($position, 1)) . '</td>'
98 - . '<td style="padding:10px;border-bottom:1px solid #f3f4f6;text-align:right;color:#374151;">' . esc_html(number_format_i18n($clicks)) . '</td>'
99 - . self::change_cell($row['change'] ?? null, $accent)
100 - . '</tr>';
67 + $out[] = [
68 + 'title' => $query,
69 + 'subtitle' => self::position_line($row['position'] ?? null, $row['prev_position'] ?? null),
70 + 'pill' => self::clicks_pill((int) ($row['change'] ?? 0)),
71 + ];
101 72 }
102 -
103 - $html .= '</table>';
104 - return $html;
73 + return $out;
105 74 }
106 75
107 76 /**
108 - * Table header cell.
77 + * "Avg. position 9.4 (was 5.1)". A null current position means the query
78 + * had no impressions this period — say so rather than print 0.0.
109 79 */
110 - private static function th(string $label, string $align): string {
111 - return '<th style="text-align:' . esc_attr($align) . ';padding:8px 10px;border-bottom:1px solid #e5e7eb;font:600 12px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;color:#6b7280;text-transform:uppercase;letter-spacing:.04em;">'
112 - . esc_html($label) . '</th>';
80 + private static function position_line($position, $previous): string {
81 + if ($position === null) {
82 + return __('No impressions this period', 'thinkrank');
83 + }
84 + $line = sprintf(
85 + /* translators: %s: average position, one decimal. */
86 + __('Avg. position %s', 'thinkrank'),
87 + number_format_i18n((float) $position, 1)
88 + );
89 + if ($previous !== null && abs((float) $previous - (float) $position) >= 0.1) {
90 + $line .= ' ' . sprintf(
91 + /* translators: %s: previous average position, one decimal. */
92 + __('(was %s)', 'thinkrank'),
93 + number_format_i18n((float) $previous, 1)
94 + );
95 + }
96 + return $line;
113 97 }
114 98
115 99 /**
116 - * Signed click-change cell. Positive renders green with ▲, negative red
117 - * with ▼. A null/zero change renders a neutral dash.
100 + * "+412 clicks" / "−188 clicks".
118 101 */
119 - private static function change_cell($change, string $accent): string {
120 - $td = '<td style="padding:10px;border-bottom:1px solid #f3f4f6;text-align:right;font:600 13px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;color:%s;">%s</td>';
121 -
122 - if ($change === null || (int) $change === 0) {
123 - return sprintf($td, '#9ca3af', '—');
124 - }
125 - $change = (int) $change;
126 - $arrow = $change > 0 ? '▲' : '▼';
127 - return sprintf($td, esc_attr($accent), esc_html($arrow . ' ' . number_format_i18n(abs($change))));
102 + private static function clicks_pill(int $change): string {
103 + $sign = $change < 0 ? '−' : '+';
104 + return $sign . sprintf(
105 + /* translators: %s: number of clicks. */
106 + _n('%s click', '%s clicks', abs($change), 'thinkrank'),
107 + number_format_i18n(abs($change))
108 + );
128 109 }
129 110 }