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 +111 -56 2.4.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,14 +26,17 @@
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 +
24 33 // This file is included from Email_Report_Renderer, so its variables live in
25 34 // that method's scope, not the global one — the prefix sniff assumes file
26 -// scope means global scope. The two unescaped echoes are the logo anchor and
27 -// the section HTML, both assembled from esc_url()/esc_html() output above and
28 -// in the section renderers; escaping them again would print entities in the
29 -// email body.
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.
30 39 // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound, WordPress.Security.EscapeOutput.OutputNotEscaped
31 40
32 41 if (!defined('ABSPATH')) {
33 42 exit;
@@ -34,8 +43,9 @@
34 43 }
35 44
36 45 $payload = isset($payload) && is_array($payload) ? $payload : [];
37 46 $site_title = (string) ($payload['site_title'] ?? '');
47 +$site_url = (string) ($payload['site_url'] ?? '');
38 48 $header_logo = (string) ($payload['header_logo'] ?? '');
39 49 $header_bg = (string) ($payload['header_bg'] ?? '');
40 50 $logo_link = (string) ($payload['logo_link'] ?? '');
41 51 $intro_text = (string) ($payload['intro_text'] ?? '');
@@ -42,28 +52,51 @@
42 52 $sections_html = (string) ($payload['sections_html'] ?? '');
43 53 $footer_text = (string) ($payload['footer_text'] ?? '');
44 54 $additional_css = (string) ($payload['additional_css'] ?? '');
45 55 $cta_url = (string) ($payload['cta_url'] ?? '');
56 +$settings_url = (string) ($payload['settings_url'] ?? '');
57 +$frequency_label = (string) ($payload['frequency_label'] ?? '');
46 58 $context = (array) ($payload['context'] ?? []);
47 59 $period_label = (string) ($context['period_label'] ?? '');
48 60 $is_test = !empty($context['is_test']);
61 +$not_connected = !empty($context['not_connected']);
49 62
50 -$header_style = 'padding:24px;text-align:center;';
51 -if ($header_bg !== '') {
52 - $header_style .= 'background:' . $header_bg . ';';
53 -} else {
54 - $header_style .= 'background:#0f172a;';
55 -}
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;
56 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 +
57 77 $logo_img = $header_logo !== ''
58 - ? '<img src="' . esc_url($header_logo) . '" alt="' . esc_attr($site_title) . '" style="max-height:48px;border:0;display:inline-block;" />'
59 - : '<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) . ';">'
60 80 . esc_html($site_title !== '' ? $site_title : 'ThinkRank')
61 81 . '</span>';
62 82
63 83 if ($logo_link !== '') {
64 - $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>';
65 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 + );
66 99 ?>
67 100 <!doctype html>
68 101 <html lang="<?php echo esc_attr(str_replace('_', '-', get_locale())); ?>">
69 102 <head>
@@ -68,8 +101,9 @@
68 101 <html lang="<?php echo esc_attr(str_replace('_', '-', get_locale())); ?>">
69 102 <head>
70 103 <meta charset="UTF-8" />
71 104 <meta name="viewport" content="width=device-width,initial-scale=1" />
105 + <meta name="color-scheme" content="light" />
72 106 <title><?php echo esc_html($site_title); ?></title>
73 107 <?php if ($additional_css !== '') : ?>
74 108 <style type="text/css">
75 109 <?php echo esc_html($additional_css); ?>
@@ -75,66 +109,87 @@
75 109 <?php echo esc_html($additional_css); ?>
76 110 </style>
77 111 <?php endif; ?>
78 112 </head>
79 -<body style="margin:0;padding:0;background:#f4f4f5;-webkit-font-smoothing:antialiased;">
80 - <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); ?>;">
81 116 <tr>
82 - <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%;">
83 119
84 - <?php if ($is_test) : ?>
85 - <table role="presentation" width="640" cellpadding="0" cellspacing="0" style="max-width:640px;width:100%;margin:0 0 12px 0;">
86 - <tr>
87 - <td style="background:#fef3c7;color:#78350f;padding:10px 16px;border-radius:6px;font:500 13px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;">
88 - <?php esc_html_e('Test email — this is a preview triggered from the ThinkRank settings.', 'thinkrank'); ?>
89 - </td>
90 - </tr>
91 - </table>
92 - <?php endif; ?>
93 -
94 - <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);">
95 120 <tr>
96 - <td style="<?php echo esc_attr($header_style); ?>">
97 - <?php echo $logo_img; // already escaped above ?>
98 - <?php if ($period_label !== '') : ?>
99 - <div style="margin-top:8px;color:rgba(255,255,255,0.85);font:500 13px/1.4 -apple-system,Segoe UI,Roboto,sans-serif;">
100 - <?php echo esc_html($period_label); ?>
101 - </div>
102 - <?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>
103 130 </td>
104 131 </tr>
105 132
106 - <?php if ($intro_text !== '') : ?>
133 + <?php if ($is_test) : ?>
107 134 <tr>
108 - <td style="padding:24px 24px 0 24px;font:400 15px/1.6 -apple-system,Segoe UI,Roboto,sans-serif;color:#374151;">
109 - <?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>
110 148 </td>
111 149 </tr>
112 150 <?php endif; ?>
113 151
114 - <tr>
115 - <td style="padding:24px;font:400 14px/1.6 -apple-system,Segoe UI,Roboto,sans-serif;color:#1f2937;">
116 - <?php echo $sections_html; // sections render their own escaped HTML ?>
117 - </td>
118 - </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; ?>
119 155
120 - <?php if ($cta_url !== '') : ?>
121 - <tr>
122 - <td align="center" style="padding:0 24px 28px 24px;">
123 - <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;">
124 - <?php esc_html_e('View Full Report in Dashboard', 'thinkrank'); ?>
125 - </a>
126 - </td>
127 - </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 + ?>
128 171 <?php endif; ?>
129 172
130 173 <tr>
131 - <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;">
132 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; ?>
133 188 </td>
134 189 </tr>
190 +
135 191 </table>
136 -
137 192 </td>
138 193 </tr>
139 194 </table>
140 195 </body>