| 1 |
<?php |
| 2 |
/** |
| 3 |
* Email Report Layout |
| 4 |
* |
| 5 |
* Receives a $payload array assembled by Email_Report_Renderer. |
| 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 |
* |
| 12 |
* Email-client safety notes: |
| 13 |
* - Inline styles only — Gmail / Outlook / Apple Mail strip / partially |
| 14 |
* support <style> blocks. Anything that must render reliably is inline. |
| 15 |
* - Tables for outer layout — most reliable across clients (especially |
| 16 |
* older Outlook versions). |
| 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. |
| 22 |
* |
| 23 |
* @var array $payload |
| 24 |
* |
| 25 |
* @package ThinkRank |
| 26 |
*/ |
| 27 |
|
| 28 |
declare(strict_types=1); |
| 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 |
|
| 41 |
if (!defined('ABSPATH')) { |
| 42 |
exit; |
| 43 |
} |
| 44 |
|
| 45 |
$payload = isset($payload) && is_array($payload) ? $payload : []; |
| 46 |
$site_title = (string) ($payload['site_title'] ?? ''); |
| 47 |
$site_url = (string) ($payload['site_url'] ?? ''); |
| 48 |
$header_logo = (string) ($payload['header_logo'] ?? ''); |
| 49 |
$header_bg = (string) ($payload['header_bg'] ?? ''); |
| 50 |
$logo_link = (string) ($payload['logo_link'] ?? ''); |
| 51 |
$intro_text = (string) ($payload['intro_text'] ?? ''); |
| 52 |
$sections_html = (string) ($payload['sections_html'] ?? ''); |
| 53 |
$footer_text = (string) ($payload['footer_text'] ?? ''); |
| 54 |
$additional_css = (string) ($payload['additional_css'] ?? ''); |
| 55 |
$cta_url = (string) ($payload['cta_url'] ?? ''); |
| 56 |
$settings_url = (string) ($payload['settings_url'] ?? ''); |
| 57 |
$frequency_label = (string) ($payload['frequency_label'] ?? ''); |
| 58 |
$context = (array) ($payload['context'] ?? []); |
| 59 |
$period_label = (string) ($context['period_label'] ?? ''); |
| 60 |
$is_test = !empty($context['is_test']); |
| 61 |
$not_connected = !empty($context['not_connected']); |
| 62 |
|
| 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; |
| 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 |
|
| 77 |
$logo_img = $header_logo !== '' |
| 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) . ';">' |
| 80 |
. esc_html($site_title !== '' ? $site_title : 'ThinkRank') |
| 81 |
. '</span>'; |
| 82 |
|
| 83 |
if ($logo_link !== '') { |
| 84 |
$logo_img = '<a href="' . esc_url($logo_link) . '" style="text-decoration:none;">' . $logo_img . '</a>'; |
| 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 |
); |
| 99 |
?> |
| 100 |
<!doctype html> |
| 101 |
<html lang="<?php echo esc_attr(str_replace('_', '-', get_locale())); ?>"> |
| 102 |
<head> |
| 103 |
<meta charset="UTF-8" /> |
| 104 |
<meta name="viewport" content="width=device-width,initial-scale=1" /> |
| 105 |
<meta name="color-scheme" content="light" /> |
| 106 |
<title><?php echo esc_html($site_title); ?></title> |
| 107 |
<?php if ($additional_css !== '') : ?> |
| 108 |
<style type="text/css"> |
| 109 |
<?php echo esc_html($additional_css); ?> |
| 110 |
</style> |
| 111 |
<?php endif; ?> |
| 112 |
</head> |
| 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); ?>;"> |
| 116 |
<tr> |
| 117 |
<td align="center" style="padding:32px 16px;"> |
| 118 |
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px;width:100%;"> |
| 119 |
|
| 120 |
<tr> |
| 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> |
| 130 |
</td> |
| 131 |
</tr> |
| 132 |
|
| 133 |
<?php if ($is_test) : ?> |
| 134 |
<tr> |
| 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> |
| 148 |
</td> |
| 149 |
</tr> |
| 150 |
<?php endif; ?> |
| 151 |
|
| 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; ?> |
| 155 |
|
| 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 |
?> |
| 171 |
<?php endif; ?> |
| 172 |
|
| 173 |
<tr> |
| 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;"> |
| 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 |
· |
| 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; ?> |
| 188 |
</td> |
| 189 |
</tr> |
| 190 |
|
| 191 |
</table> |
| 192 |
</td> |
| 193 |
</tr> |
| 194 |
</table> |
| 195 |
</body> |
| 196 |
</html> |
| 197 |
|