PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.8.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.8.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 1.0.1 1.0.2 1.1.0 All 49 releases
← All changes | templates/email-report/email.html.php +115 -52 1.28.02.8.0 View file →
@@ -3,17 +3,23 @@
3 3 * Email Report Layout
4 4 *
5 5 * Receives a $payload array assembled by Email_Report_Renderer.
6 6 *
7 + * A light canvas with one centred 600px column: the logo row, an optional
8 + * test-send banner, an optional intro, the section cards (each already a
9 + * <tr> from Email_Report_Renderer::card()), the dashboard call to action
10 + * and a quiet footer (#742).
11 + *
7 12 * Email-client safety notes:
8 13 * - Inline styles only — Gmail / Outlook / Apple Mail strip / partially
9 14 * support <style> blocks. Anything that must render reliably is inline.
10 15 * - Tables for outer layout — most reliable across clients (especially
11 16 * older Outlook versions).
12 - * - The `additional_css` block (Pro-only) is injected as a <style> tag so
13 - * advanced agencies can theme without us re-engineering inline rules
14 - * every time. The PRD calls out that CSS support varies — that's their
15 - * risk to take.
17 + * - The header logo, logo link, header background, intro, footer, extra CSS
18 + * and dashboard link are slots filled through the
19 + * `thinkrank_email_report_payload` filter. A header background turns the
20 + * logo row into a coloured band. Extra CSS goes into a <style> tag; CSS
21 + * support varies across email clients.
16 22 *
17 23 * @var array $payload
18 24 *
19 25 * @package ThinkRank
@@ -20,8 +26,19 @@
20 26 */
21 27
22 28 declare(strict_types=1);
23 29
30 +use ThinkRank\SEO\Email_Report_Renderer;
31 +use ThinkRank\SEO\Email_Report_Sections\Email_Report_Html;
32 +
33 +// This file is included from Email_Report_Renderer, so its variables live in
34 +// that method's scope, not the global one — the prefix sniff assumes file
35 +// scope means global scope. The unescaped echoes are the logo anchor, the
36 +// section HTML and the cards, all assembled from esc_url()/esc_html() output
37 +// above and in the section renderers; escaping them again would print
38 +// entities in the email body.
39 +// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound, WordPress.Security.EscapeOutput.OutputNotEscaped
40 +
24 41 if (!defined('ABSPATH')) {
25 42 exit;
26 43 }
27 44
@@ -26,8 +43,9 @@
26 43 }
27 44
28 45 $payload = isset($payload) && is_array($payload) ? $payload : [];
29 46 $site_title = (string) ($payload['site_title'] ?? '');
47 +$site_url = (string) ($payload['site_url'] ?? '');
30 48 $header_logo = (string) ($payload['header_logo'] ?? '');
31 49 $header_bg = (string) ($payload['header_bg'] ?? '');
32 50 $logo_link = (string) ($payload['logo_link'] ?? '');
33 51 $intro_text = (string) ($payload['intro_text'] ?? '');
@@ -34,28 +52,51 @@
34 52 $sections_html = (string) ($payload['sections_html'] ?? '');
35 53 $footer_text = (string) ($payload['footer_text'] ?? '');
36 54 $additional_css = (string) ($payload['additional_css'] ?? '');
37 55 $cta_url = (string) ($payload['cta_url'] ?? '');
56 +$settings_url = (string) ($payload['settings_url'] ?? '');
57 +$frequency_label = (string) ($payload['frequency_label'] ?? '');
38 58 $context = (array) ($payload['context'] ?? []);
39 59 $period_label = (string) ($context['period_label'] ?? '');
40 60 $is_test = !empty($context['is_test']);
61 +$not_connected = !empty($context['not_connected']);
41 62
42 -$header_style = 'padding:24px;text-align:center;';
43 -if ($header_bg !== '') {
44 - $header_style .= 'background:' . $header_bg . ';';
45 -} else {
46 - $header_style .= 'background:#0f172a;';
47 -}
63 +$font = Email_Report_Html::FONT;
64 +$ink = Email_Report_Html::INK;
65 +$muted = Email_Report_Html::MUTED;
66 +$canvas = Email_Report_Html::CANVAS;
67 +$brand = Email_Report_Html::PRIMARY;
48 68
69 +// Logo row. With a Pro header background it becomes a coloured band and the
70 +// right-hand label goes white; otherwise it sits straight on the canvas.
71 +$band = $header_bg !== '';
72 +$header_style = $band
73 + ? 'padding:18px 24px;background:' . esc_attr($header_bg) . ';border-radius:14px;'
74 + : 'padding:0 4px;';
75 +$label_color = $band ? '#ffffff' : $muted;
76 +
49 77 $logo_img = $header_logo !== ''
50 - ? '<img src="' . esc_url($header_logo) . '" alt="' . esc_attr($site_title) . '" style="max-height:48px;border:0;display:inline-block;" />'
51 - : '<span style="font:700 22px/1 -apple-system,Segoe UI,Roboto,sans-serif;color:#ffffff;">'
78 + ? '<img src="' . esc_url($header_logo) . '" alt="' . esc_attr($site_title !== '' ? $site_title : 'ThinkRank') . '" height="32" style="display:block;border:0;max-height:32px;width:auto;" />'
79 + : '<span style="' . $font . 'font-size:20px;font-weight:800;letter-spacing:-0.02em;color:' . ($band ? '#ffffff' : $ink) . ';">'
52 80 . esc_html($site_title !== '' ? $site_title : 'ThinkRank')
53 81 . '</span>';
54 82
55 83 if ($logo_link !== '') {
56 - $logo_img = '<a href="' . esc_url($logo_link) . '" style="text-decoration:none;color:#ffffff;">' . $logo_img . '</a>';
84 + $logo_img = '<a href="' . esc_url($logo_link) . '" style="text-decoration:none;">' . $logo_img . '</a>';
57 85 }
86 +
87 +$preheader = $not_connected
88 + ? sprintf(
89 + /* translators: %s: site title. */
90 + __('Connect Google Search Console to start receiving SEO reports for %s.', 'thinkrank'),
91 + $site_title
92 + )
93 + : sprintf(
94 + /* translators: 1: site title, 2: period label. */
95 + __('%1$s: your search performance for %2$s.', 'thinkrank'),
96 + $site_title,
97 + $period_label
98 + );
58 99 ?>
59 100 <!doctype html>
60 101 <html lang="<?php echo esc_attr(str_replace('_', '-', get_locale())); ?>">
61 102 <head>
@@ -60,8 +101,9 @@
60 101 <html lang="<?php echo esc_attr(str_replace('_', '-', get_locale())); ?>">
61 102 <head>
62 103 <meta charset="UTF-8" />
63 104 <meta name="viewport" content="width=device-width,initial-scale=1" />
105 + <meta name="color-scheme" content="light" />
64 106 <title><?php echo esc_html($site_title); ?></title>
65 107 <?php if ($additional_css !== '') : ?>
66 108 <style type="text/css">
67 109 <?php echo esc_html($additional_css); ?>
@@ -67,66 +109,87 @@
67 109 <?php echo esc_html($additional_css); ?>
68 110 </style>
69 111 <?php endif; ?>
70 112 </head>
71 -<body style="margin:0;padding:0;background:#f4f4f5;-webkit-font-smoothing:antialiased;">
72 - <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f4f4f5;">
113 +<body style="margin:0;padding:0;background:<?php echo esc_attr($canvas); ?>;-webkit-font-smoothing:antialiased;">
114 + <div style="display:none;max-height:0;overflow:hidden;opacity:0;mso-hide:all;"><?php echo esc_html($preheader); ?></div>
115 + <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:<?php echo esc_attr($canvas); ?>;">
73 116 <tr>
74 - <td align="center" style="padding:32px 12px;">
117 + <td align="center" style="padding:32px 16px;">
118 + <table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px;width:100%;">
75 119
76 - <?php if ($is_test) : ?>
77 - <table role="presentation" width="640" cellpadding="0" cellspacing="0" style="max-width:640px;width:100%;margin:0 0 12px 0;">
78 - <tr>
79 - <td style="background:#fef3c7;color:#78350f;padding:10px 16px;border-radius:6px;font:500 13px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;">
80 - <?php esc_html_e('Test email — this is a preview triggered from the ThinkRank settings.', 'thinkrank'); ?>
81 - </td>
82 - </tr>
83 - </table>
84 - <?php endif; ?>
85 -
86 - <table role="presentation" width="640" cellpadding="0" cellspacing="0" style="max-width:640px;width:100%;background:#ffffff;border-radius:12px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,0.08);">
87 120 <tr>
88 - <td style="<?php echo esc_attr($header_style); ?>">
89 - <?php echo $logo_img; // already escaped above ?>
90 - <?php if ($period_label !== '') : ?>
91 - <div style="margin-top:8px;color:rgba(255,255,255,0.85);font:500 13px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;">
92 - <?php echo esc_html($period_label); ?>
93 - </div>
94 - <?php endif; ?>
121 + <td style="padding:0 0 20px 0;">
122 + <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td style="<?php echo $header_style; ?>">
123 + <table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr>
124 + <td valign="middle"><?php echo $logo_img; ?></td>
125 + <?php if ($frequency_label !== '') : ?>
126 + <td align="right" valign="middle" style="<?php echo $font; ?>font-size:12px;color:<?php echo esc_attr($label_color); ?>;white-space:nowrap;"><?php echo esc_html($frequency_label); ?></td>
127 + <?php endif; ?>
128 + </tr></table>
129 + </td></tr></table>
95 130 </td>
96 131 </tr>
97 132
98 - <?php if ($intro_text !== '') : ?>
133 + <?php if ($is_test) : ?>
99 134 <tr>
100 - <td style="padding:24px 24px 0 24px;font:400 15px/1.6 -apple-system,Segoe UI,Roboto,sans-serif;color:#374151;">
101 - <?php echo wp_kses_post($intro_text); ?>
135 + <td style="padding:0 0 16px 0;">
136 + <div style="<?php echo $font; ?>font-size:13px;color:#7a4b00;background:#fff4d6;border:1px solid #f5d98a;border-radius:10px;padding:10px 14px;">
137 + <?php
138 + // The connect state is not what a scheduled run sends: while
139 + // Search Console is disconnected nothing goes out at all, so
140 + // promising "the same layout" here contradicts the card below.
141 + echo esc_html(
142 + $not_connected
143 + ? __('This is a test email sent from the ThinkRank dashboard. Scheduled reports stay paused until Search Console is connected.', 'thinkrank')
144 + : __('This is a test email sent from the ThinkRank dashboard. Scheduled reports use the same layout.', 'thinkrank')
145 + );
146 + ?>
147 + </div>
102 148 </td>
103 149 </tr>
104 150 <?php endif; ?>
105 151
106 - <tr>
107 - <td style="padding:24px;font:400 14px/1.6 -apple-system,Segoe UI,Roboto,sans-serif;color:#1f2937;">
108 - <?php echo $sections_html; // sections render their own escaped HTML ?>
109 - </td>
110 - </tr>
152 + <?php if ($intro_text !== '') : ?>
153 + <?php echo Email_Report_Renderer::card('<div style="' . $font . 'font-size:15px;line-height:1.6;color:' . $ink . ';">' . wp_kses_post(wpautop($intro_text)) . '</div>', 'tr-email-intro'); ?>
154 + <?php endif; ?>
111 155
112 - <?php if ($cta_url !== '') : ?>
113 - <tr>
114 - <td align="center" style="padding:0 24px 28px 24px;">
115 - <a href="<?php echo esc_url($cta_url); ?>" style="display:inline-block;background:#2563eb;color:#ffffff;text-decoration:none;font:600 14px/1 -apple-system,Segoe UI,Roboto,sans-serif;padding:12px 22px;border-radius:8px;">
116 - <?php esc_html_e('View Full Report', 'thinkrank'); ?>
117 - </a>
118 - </td>
119 - </tr>
156 + <?php echo $sections_html; ?>
157 +
158 + <?php if ($cta_url !== '' && !$not_connected) : ?>
159 + <?php
160 + echo Email_Report_Renderer::card(
161 + '<table role="presentation" width="100%" cellpadding="0" cellspacing="0"><tr><td align="center" style="padding:6px 0 2px 0;">'
162 + . '<a href="' . esc_url($cta_url) . '" style="display:inline-block;background:' . $brand . ';color:#ffffff;' . $font . 'font-size:15px;font-weight:700;text-decoration:none;padding:14px 28px;border-radius:10px;">'
163 + . esc_html__('Open the full report in ThinkRank', 'thinkrank') . '</a>'
164 + . '<div style="' . $font . 'font-size:12px;color:' . $muted . ';margin-top:12px;">'
165 + . esc_html__('Page-by-page metrics, keyword history and AI suggestions live in your dashboard.', 'thinkrank')
166 + . '</div></td></tr></table>',
167 + 'tr-email-cta',
168 + '22px 28px'
169 + );
170 + ?>
120 171 <?php endif; ?>
121 172
122 173 <tr>
123 - <td style="padding:18px 24px;background:#f9fafb;color:#6b7280;font:400 12px/1.5 -apple-system,Segoe UI,Roboto,sans-serif;text-align:center;border-top:1px solid #e5e7eb;">
174 + <td style="padding:8px 4px 0 4px;<?php echo $font; ?>font-size:12px;color:<?php echo esc_attr($muted); ?>;line-height:1.7;text-align:center;">
124 175 <?php echo wp_kses_post($footer_text); ?>
176 + <?php if ($settings_url !== '' || $site_url !== '') : ?>
177 + <br>
178 + <?php if ($settings_url !== '') : ?>
179 + <a href="<?php echo esc_url($settings_url); ?>" style="color:<?php echo esc_attr($muted); ?>;"><?php echo esc_html__('Report settings', 'thinkrank'); ?></a>
180 + <?php endif; ?>
181 + <?php if ($settings_url !== '' && $site_url !== '') : ?>
182 + &nbsp;·&nbsp;
183 + <?php endif; ?>
184 + <?php if ($site_url !== '') : ?>
185 + <a href="<?php echo esc_url($site_url); ?>" style="color:<?php echo esc_attr($muted); ?>;"><?php echo esc_html(wp_parse_url($site_url, PHP_URL_HOST) ?: $site_url); ?></a>
186 + <?php endif; ?>
187 + <?php endif; ?>
125 188 </td>
126 189 </tr>
190 +
127 191 </table>
128 -
129 192 </td>
130 193 </tr>
131 194 </table>
132 195 </body>