From 2bb9aaaf24b794b528e3894ee9f9c42ca6d7fe93 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 01 Jan 2026 21:08:58 +0000
Subject: [PATCH] =FeedRoutes: extractTaxonomies added
---
inc/managers/EmailManager.php | 681 ++++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 485 insertions(+), 196 deletions(-)
diff --git a/inc/managers/EmailManager.php b/inc/managers/EmailManager.php
index d7d0368..106a387 100644
--- a/inc/managers/EmailManager.php
+++ b/inc/managers/EmailManager.php
@@ -24,17 +24,15 @@
class EmailManager
{
- // Brand colors
- private string $action_color = '#FF0080';
- private string $bg_light = '#F9F9F9';
- private string $bg_dark = '#1B1B1B';
- private string $text_color = '#151515';
- private string $text_light = '#F9F9F9';
+ public array $colours = JVB_EMAIL['colours'];
+ private string $title = JVB_EMAIL['content']['title'];
+ private string $prefix = JVB_EMAIL['content']['subjectPrefix'];
+ private string $signature = JVB_EMAIL['content']['signature'];
+ private string $footer;
// Site info
private string $site_name;
private string $site_url;
- private string $signature;
/**
* Constructor - sets up all filters
@@ -43,7 +41,7 @@
{
$this->site_name = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
$this->site_url = get_site_url();
- $this->signature = jvbSignature();
+ $this->footer = (is_array(JVB_EMAIL['content']['footer'])) ? implode('', JVB_EMAIL['content']['footer']) : JVB_EMAIL['content']['footer'];
add_filter('wp_mail_content_type', [$this, 'setHtmlContentType']);
// User registration emails
@@ -84,7 +82,7 @@
* @param string $header Optional header text for the template
* @return bool Whether the email was sent successfully
*/
- public function sendEmail(string $to, string $subject, string $message, string $header = ''):bool
+ public function sendEmail(string $to, string $subject, string $message, string $header = '', array $headers = [], array $attachments = []):bool
{
// Make sure the content type is set to HTML
add_filter('wp_mail_content_type', [$this, 'setHtmlContentType']);
@@ -93,7 +91,7 @@
$formatted_message = $this->getEmailTemplate($message, $header);
// Send the email
- $result = wp_mail($to, $subject, $formatted_message);
+ $result = wp_mail($to, $subject, $formatted_message, $headers, $attachments);
// Reset content type filter to avoid affecting other emails
remove_filter('wp_mail_content_type', [$this, 'setHtmlContentType']);
@@ -107,14 +105,17 @@
*
* @return string
*/
- private function getEmailTemplate(string $content, string $header = ''):string
+ private function getEmailTemplate(string $content, string $headerText = ''):string
{
- // Get logo URL (replace with your actual logo path)
- $logo_url = esc_url(wp_get_attachment_image_url(get_theme_mod('custom_logo'), 'medium'));
+ $custom_logo_id = get_theme_mod( 'custom_logo' );
+ $logo_thumbnail = wp_get_attachment_image_src( $custom_logo_id, 'custom-logo-thumbnail' );
+
+ $logo = ($logo_thumbnail) ? '<img src="' . esc_url( $logo_thumbnail[0] ) . '" alt="Site Logo" width="150" height="150" style="max-width:120px;height:auto;">' : '';
+
// Default header if none provided
- if (empty($header)) {
- $header = 'edmonton.ink';
+ if (empty($headerText)) {
+ $headerText = $this->title;
}
return '<!DOCTYPE html>
@@ -122,17 +123,38 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>' . $this->site_name . '</title>
- <style>
+ <title>' . $this->title . ' | ' . $this->site_name .'</title>
+ </head>
+ <body style="font-family:Segoe UI, Tahoma, Geneva, Verdana, sans-serif;background-color: '.$this->colours['light'].';color:'.$this->colours['dark'].';line-height:1.5;margin:0;padding:0;">
+ <div style="background-color:'.$this->colours['dark'].';color:'.$this->colours['light'].';padding:5px;text-align:center;font-size:24px;font-weight:900;letter-spacing:1.5px;text-transform:uppercase;border-radius:5px 5px 0 0;">
+ <a href="' . $this->site_url . '" style="display:inline-block;vertical-align:middle;">
+ ' . $logo . '
+ </a>
+ <p style="display:inline-block;vertical-align:middle;margin: 0 auto 0 0;">' . $headerText . '</p>
+ </div>
+ <div style="padding:4rem 0;width:100%;max-width:600px;margin:0 auto;">
+ ' . $content . '
+ '.$this->signature().'
+ </div>
+ <div style="border-radius:0 0 5px 5px;text-align:center;margin-top:20px;font-size:12px;background-color:'.$this->colours['light-200'].'; color: '.$this->colours['dark-200'].';padding:10px;line-height:1.6;">
+ ' . $this->footer . '
+ </div>
+ </body>
+ </html>';
+ }
+
+ private function oldStyle(){
+ $oldStyle ='
+ <style>
body, table, td, p, a, li, blockquote {
line-height: 1.5;
}
body {
- background-color: ' . $this->bg_light . ';
+ background-color: ' . $this->colours['light'] . ';
margin: 0;
padding: 0;
- color: ' . $this->text_color . ';
+ color: ' . $this->colours['dark'] . ';
}
.email-container {
@@ -147,10 +169,10 @@
}
.header {
- background-color: ' . $this->bg_dark . ';
+ background-color: ' . $this->colours['dark'] . ';
padding: 5px;
text-align: center;
- color: ' . $this->text_light . ';
+ color: ' . $this->colours['light'] . ';
font-size: 24px;
font-weight: 900;
letter-spacing: 1.5px;
@@ -164,12 +186,11 @@
vertical-align: middle;
}
.header p {
- margin: 0;
- margin-left: auto;
+ margin: 0 auto 0 0;
}
.content {
- background-color: #ffffff;
+ background-color: ' . $this->colours['light-50'] . ';
padding: 30px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
@@ -178,8 +199,8 @@
.button {
display: inline-block;
padding: 12px 24px;
- background-color: ' . $this->action_color . ';
- color: ' . $this->text_light . ' !important;
+ background-color: ' . $this->colours['action-0'] . ';
+ color: ' . $this->colours['action-contrast'] . ' !important;
text-decoration: none;
border-radius: 3px;
font-weight: bold;
@@ -189,7 +210,7 @@
}
.text-link {
- color: ' . $this->action_color . ';
+ color: ' . $this->colours['action-0'] . ';
text-decoration: underline;
}
@@ -197,13 +218,26 @@
text-align: center;
margin-top: 20px;
font-size: 12px;
- color: #666666;
+ background-color: ' . $this->colours['light-200'] . ';
+ color: ' . $this->colours['dark-200'] . ';
padding: 10px;
line-height: 1.6;
}
+ .notice {
+ text-align: center;
+ border-radius: 0 8px 8px 0;
+ margin: 1rem 0;
+ border-left: 4px solid '.$this->colours['action-0'].';
+ padding: 1rem;
+ background-color: '.$this->colours['light-100'].';
+ }
+ .notice strong {
+ color: '.$this->colours['action-0'].';
+ }
+
.divider {
- border-top: 1px solid #dddddd;
+ border-top: 1px solid ' . $this->colours['dark-200'] . ';
margin: 25px 0;
}
@@ -282,101 +316,50 @@
/* Dark mode support for clients that support it */
@media (prefers-color-scheme: dark) {
- .email-container {
- background-color: #1B1B1B !important;
+ .body {
+ background-color: ' . $this->colours['dark'] . ';
+ color: ' . $this->colours['light'] . ';
+ }
+
+ .header {
+ background-color: ' . $this->colours['light'] . ';
+ color: ' . $this->colours['dark'] . ';
}
.content {
- background-color: #2A2A2A !important;
- color: #F9F9F9 !important;
- }
-
- h1, h2, h3, p, li, a.text-link {
- color: #F9F9F9 !important;
+ background-color: ' . $this->colours['dark-50'] . ';
}
.footer {
- color: #999999 !important;
- }
-
- .divider {
- border-color: #444444 !important;
+ background-color: ' . $this->colours['dark-200'] . ';
+ color: ' . $this->colours['light-200'] . ';
}
}
</style>
- </head>
- <body>
- <div class="email-container">
-
- <div class="header">
- <a href="' . $this->site_url . '">
- <img src="' . $logo_url . '" alt="Edmonton Ink">
- </a>
- <p>' . $header . '</p>
- </div>
- <div class="content">
- ' . $content . '
- </div>
- <div class="footer">
- <p>© ' . date('Y') . ' edmonton.ink — Your tattoo scene on your screen.</p>
- <p><a href="' . $this->site_url . '" class="text-link">edmonton.ink</a></p>
- </div>
- </div>
- </body>
- </html>';
- }
+ ';
+ }
/**
* New user registration email to user
*/
public function customizeNewUserEmail($wp_new_user_notification_email, $user, $blogname)
{
- $user_login = $user->user_login;
-
- // Only create the password key if the user can change their password
- $key = get_password_reset_key($user);
- if (!is_wp_error($key)) {
- $reset_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
-
- $message = sprintf('<p>Hey %s!</p>', $user->first_name);
- $message .= '<p>Thanks for joining Edmonton\'s tattoo scene. Here\'s your login information:</p>';
- $message .= sprintf('<p><strong>Username:</strong> %s</p>', $user_login);
- $message .= '<p>To set your password and access your account, click the button below:</p>';
- $message .= sprintf('<p style="text-align: center;"><a href="%s" class="button">Set Your Password</a></p>', $reset_url);
- $message .= '<p>Or copy and paste this link into your browser:</p>';
- $message .= sprintf('<p style="user-select:all;">%s</p>', $reset_url);
- $message .= '<p>This link will expire in 24 hours, for security reasons.</p>';
- if (in_array('jvb_artist', array_values($user->roles))) {
- $message .= '<div class="divider"></div>';
- $message .= '<h3>NOTE:</h3>
- <p>Once you set your password, you\'ll have access to your custom dashboard where you can:</p>
- <ul>
- <li>Manage your profile information</li>
- <li>Upload tattoos/piercings, and artwork</li>
- </ul>
- <p>Nothing will be published until you\'ve been approved by 3 already approved artists, or the admin.</p>
- <p>Admins check every day or three, but, if you are in a rush, you can contact us directly by replying to this email, or texting us at 825-925-9916.</p>';
- } elseif (in_array('jvb_partner', array_values($user->roles))) {
- $message .= '<div class="divider"></div>';
- $message .= '<h3>NOTE:</h3>
- <p>Once you set your password, you\'ll have access to your custom dashboard where you can:</p>
- <ul>
- <li>Manage your profile information</li>
- <li>Create offers for enthusiasts or partners or both</li>
- </ul>
- <p>Nothing will be published until you\'ve been approved by the admin.</p>
- <p>Admins check every day or three, but, if you are in a rush, you can contact us directly by replying to this email, or texting us at 825-925-9916.</p>
- <p><strong>Note:</strong>Even after approval by admin, your ability to publish depends on your karmic standing by artists. Artists each have a vote they can cast (UP or DOWN) - if your karmic score dips too far in the negative, you account is subject to reconsideration or even a ban.</p>';
- }
- $message .= '<div class="divider"></div>';
- $message .= '<p>If you didn\'t create this account, please ignore this email and the link will expire.</p>';
- $message .= sprintf('<p>Ink on, %s</p>', $user->first_name);
- $message .= $this->signature;
- $wp_new_user_notification_email['message'] = $this->getEmailTemplate($message, 'Welcome to edmonton.ink');
- }
+ $message = sprintf(
+ '<p>Hi %s!</p>
+ <p>Thanks for signing up for an account on %s.</p>
+ <p><b>Your username:</b> %s</p>
+ <p><b>Your password:</b> <i>Your chosen password.</i></p>',
+ $user->display_name,
+ get_bloginfo('name'),
+ $user->user_login,
+ );
+ $message = apply_filters('jvbNewUserEmail', $message, $user);
+ $wp_new_user_notification_email['message'] = $this->getEmailTemplate($message, JVB_EMAIL['types']['newUser']['subject']?:'New User');
// Change the subject line
- $wp_new_user_notification_email['subject'] = sprintf('Welcome to %s! Confirm your account', $this->site_name);
+ $prefix = JVB_EMAIL['types']['newUser']['showPrefix']??true;
+ $prefix = ($prefix) ? $this->prefix : '';
+ $wp_new_user_notification_email['subject'] = $prefix.JVB_EMAIL['types']['newUser']['subject']?:'New User';
return $wp_new_user_notification_email;
}
@@ -392,12 +375,11 @@
public function customizeNewUserEmailAdmin(array $emailData, WP_User $user, string $blogname):array
{
$message = '<p>A new user has registered on <strong>' . $this->site_name . '</strong>:</p>';
- $message .= '<p><strong>Username:</strong> ' . $user->user_login . '<br>';
- $message .= '<strong>Email:</strong> ' . $user->user_email . '<br>';
- $message .= '<strong>Role:</strong> ' . str_replace(BASE, '', array_values($user->roles)[0]).'</p>';
- $message .= $this->signature;
+ $message .= '<p><strong>Username:</strong> ' . $user->user_login . '</p>';
+ $message .= '<p><strong>Email:</strong> ' . $user->user_email . '</p>';
+ $message = apply_filters('jvbNewUserAdminEmail', $message, $user);
$emailData['message'] = $this->getEmailTemplate($message, 'New User Registration');
- $emailData['subject'] = '[' . $this->site_name . '] New ' . str_replace(BASE, '', array_values($user->roles)[0]).': '.$user->display_name;
+ $emailData['subject'] = $this->prefix .'New ' . str_replace(BASE, '', array_values($user->roles)[0]).': '.$user->display_name;
return $emailData;
}
@@ -411,21 +393,27 @@
*
* @return string
*/
- public function customizePasswordResetEmail(string $message, string $key, string $user_login, WP_User $user_data):string
+ public function customizePasswordResetEmail(string $message, string $key, string $user_login, WP_User $user):string
{
- $reset_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
-
- $content = '<p>Hey bud,</p>';
- $content = '<p>We received a request to reset the password for an account associated with this email:</p>';
- $content .= '<p><strong>Username:</strong> ' . $user_login . '</p>';
- $content .= '<p>If you didn\'t make this request, you can safely ignore this email and nothing will happen to your account.</p>';
- $content .= '<p>To reset your password, click the button below:</p>';
- $content .= '<p style="text-align: center;"><a href="' . $reset_url . '" class="button">Reset Password</a></p>';
- $content .= '<p>Or copy and paste this link into your browser:</p>';
- $content .= '<p style="user-select:all;">' . $reset_url . '</p>';
- $content .= '<div class="divider"></div>';
- $content .= '<p>This password reset link is only valid for 24 hours.</p>';
- $content .= $this->signature;
+ $reset_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
+ $content = sprintf(
+ '<p>Hi %s!</p>
+ <p>We received a request to reset the password for an account associated with this email:</p>
+ <p><strong>Username:</strong> %s</p>
+ <p>If you didn\'t make this request, you can safely ignore this email and nothing will happen to your account.</p>
+ <p>To reset your password, click the button below:</p>
+ %s
+ <p>Or copy and paste this link into your browser:</p>
+ %s
+ %s
+ <p>This password reset link is only valid for 24 hours.</p>',
+ $user->display_name,
+ $user_login,
+ $this->button($reset_url,'Reset Password'),
+ $this->link($reset_url),
+ $this->divider()
+ );
+ $content = apply_filters('jvbPasswordResetEmail', $content, $user_login, $user, $reset_url);
return $this->getEmailTemplate($content, 'Password Reset');
}
@@ -438,29 +426,42 @@
*/
public function customizePasswordResetTitle(string $title, string $user_login, WP_User $user_data):string
{
- return '[' . $this->site_name . '] Password Reset Request';
- }
+ $prefix = JVB_EMAIL['types']['resetPass']['showPrefix']??true;
+ $prefix = ($prefix) ? $this->prefix : '';
+ return $prefix.JVB_EMAIL['types']['resetPass']['subject']?:'Password Reset';
+
+ }
/**
* Email change notification to admin
* @param array $email_change_email
- * @param array $user
- * @param array $userdata
+ * @param array $oldUser
+ * @param array $newUser
*
* @return array
*/
- public function customizeEmailChangeEmail(array $email_change_email, array $user, array $userdata):array
+ public function customizeEmailChangeEmail(array $email_change_email, array $oldUser, array $newUser):array
{
- $content = sprintf('<p>Hi %s,</p>', $user['first_name']);
- $content .= '<p>Ideally you already know this: you asked to change your email, and here we are.</p>';
- $content .= '<p><strong>Old Email:</strong> ' . $email_change_email['to'] . '<br>';
- $content .= '<strong>New Email:</strong> ' . $user['user_email'] . '</p>';
-
- $content .= '<p>If this is news, or you did not change your email, please contact us immediately. You can <a href="sms:+18258239916">text us</a> or reply to this email."></a></p>';
- $content .= '<p style="text-align: center;"><a href="' . wp_login_url() . '" class="button">Log In to Your Account</a></p>';
- $content .= $this->signature;
+ $content = sprintf(
+ '<p>Hi %s,</p>
+ <p>Ideally you already know this: someone asked to change the email for your account.</p>
+ <p><strong>Old Email:</strong> %s</p>
+ <p><strong>New Email:</strong> %s</p>
+ %s
+ <p>If this is news to you, or you did not request this - please contact us immediately. You can <a href="sms:+18258239916">text us</a> or reply to this email."></a></p>
+ %s',
+ $newUser['first_name'],
+ $oldUser['user_email'],
+ $newUser['user_email'],
+ $this->divider(),
+ $this->button(wp_login_url(), 'Log In To Your Account')
+ );
+ $content = apply_filters('jvbEmailChangeRequestEmail', $content, $oldUser, $newUser);
$email_change_email['message'] = $this->getEmailTemplate($content, 'Email Address Changed');
- $email_change_email['subject'] = '[' . $this->site_name . '] Email Address Changed';
+ $prefix = JVB_EMAIL['types']['emailChange']['showPrefix']??true;
+ $prefix = ($prefix) ? $this->prefix : '';
+ $email_change_email['subject'] = $prefix.JVB_EMAIL['types']['emailChange']['subject']?:'Email Address Changed';
+
return $email_change_email;
}
@@ -472,24 +473,25 @@
*
* @return string
*/
- public function customizeNewUserEmailContent(string $email_text, array $user):string
+ public function customizeNewUserEmailContent(string $email_text, array $new_user_email):string
{
+ $confirm_url = esc_url( add_query_arg('newuseremail', $new_user_email['hash'], self_admin_url('profile.php')));
+ $content = sprintf(
+ '<p>Hey,</p>
+ <p>There was a request to change the email address associated with your account to this one.</p>
+ <p>This is just a friendly email to ensure you would like this change.</p>
+ <p>You can confirm this change by clicking the button below:</p>
+ %s
+ <p>Or copy and paste this link into your browser:</p>
+ %s',
+ $this->button($confirm_url, 'Confirm this Email'),
+ $this->link($confirm_url)
+ );
- // Extract the confirmation link from the default message
- $pattern = '/###CONFIRM_URL### (.*?)(?=\n|\r|$)/';
- preg_match($pattern, $email_text, $matches);
- $confirm_url = isset($matches[1]) ? $matches[1] : admin_url('profile.php?newuseremail=' . $user['hash']);
+ $content = apply_filters('jvbEmailChangedEmail', $content, $confirm_url);
- $content = '<p>Hey human,</p>';
- $content .= '<p>Seems you want to change the email associated with your account.</p>';
- $content .= '<p>You\'d like to change your email to: <strong>' . $user['newemail'] . '</strong></p>';
- $content .= '<p>Please confirm this change by clicking the button below:</p>';
- $content .= '<p style="text-align: center;"><a href="' . $confirm_url . '" class="button">Confirm Email Change</a></p>';
- $content .= '<p>Or copy and paste this link into your browser:</p>';
- $content .= '<p style="user-select:all">' . $confirm_url . '</p>';
- $content .= '<div class="divider"></div>';
+ $content .= $this->divider();
$content .= '<p>If you did not request this change, you can safely ignore this email and nothing will change.</p>';
- $content .= $this->signature;
return $this->getEmailTemplate($content, 'Confirm Email Change');
}
@@ -497,23 +499,28 @@
/**
* Password change notification
* @param array $pass_change_email
- * @param array $user
- * @param array $userdata
+ * @param array $oldUser
+ * @param array $newUser
*
* @return array
*/
- public function customizePasswordChangeEmail(array $pass_change_email, array $user, array $userdata):array
+ public function customizePasswordChangeEmail(array $pass_change_email, array $oldUser, array $newUser):array
{
-
- $content = '<p>Hey bud,</p>';
- $content .= '<p>Ideally, you\'re expecting this email. You wanted to change your password, and this is to let you know that it\'s definitely updated.</p>';
- $content .= '<p>If you did not change your password, please contact us immediately.</p>';
- $content .= '<p>You can <a href="sms:+18259257398">text us</a>, or reply to this email.</p>';
- $content .= '<p style="text-align: center;"><a href="' . wp_login_url() . '" class="button">Log In to Your Account</a></p>';
- $content .= $this->signature;
+ $content = sprintf(
+ '<p>Hi %s,</p>
+ <p>This is a confirmation email to let you know your password has successfully been changed.</p>
+ <p>If you\'re not expecting this email, and did not change your password - please <strong>contact us immediately</strong></p>
+ <p>You can <a href="sms:+18259257398">text us</a>, or reply to this email.</p>
+ %s',
+ $oldUser['first_name'],
+ $this->button(wp_login_url(), 'Log In to Your Account')
+ );
+ $content = apply_filters('jvbPasswordChangeEmail', $content, $oldUser, $newUser);
$pass_change_email['message'] = $this->getEmailTemplate($content, 'Password Changed');
- $pass_change_email['subject'] = '[' . $this->site_name . '] Password Changed';
+ $prefix = JVB_EMAIL['types']['passwordChange']['showPrefix']??true;
+ $prefix = ($prefix) ? $this->prefix : '';
+ $pass_change_email['subject'] = $prefix.JVB_EMAIL['types']['passwordChange']['subject']?:'Password Changed';
return $pass_change_email;
}
@@ -525,16 +532,11 @@
*
* @return string
*/
- public function customizeUserRequestEmail(string $content, array $request_data):string
+ public function customizeUserRequestEmail(string $content, array $email_data):string
{
- // Extract the confirmation URL from the content
- $pattern = '/<a href="([^"]+)"[^>]*>/';
- preg_match($pattern, $content, $matches);
- $confirm_url = isset($matches[1]) ? $matches[1] : '';
-
- $request_type = $request_data['action_name'];
- $request_name = '';
+ $confirm_url = $email_data['confirm_url'];
+ $request_type = $email_data['action_name'];
switch ($request_type) {
case 'export_personal_data':
@@ -547,16 +549,22 @@
$request_name = 'Data Request';
}
- $message = '<p>Hi privacy enthusiast,</p>';
- $message .= '<p>A request has been made to perform the following action on your account:</p>';
- $message .= '<p><strong>' . $request_name . '</strong></p>';
- $message .= '<p>If you made this request, you can confirm it by clicking the button below:</p>';
- $message .= '<p style="text-align: center;"><a href="' . $confirm_url . '" class="button">Confirm Request</a></p>';
- $message .= '<p>Or copy and paste this link into your browser:</p>';
- $message .= '<p style="user-select:all;">' . $confirm_url . '</p>';
- $message .= '<div class="divider"></div>';
+
+ $message = sprintf(
+ '<p>Hi,</p>
+ <p>You\'re receiving this email because a request has been made to <strong>%s</strong></p>
+ <p>If you\'re the one who made this request, you can confirm it by clicking the button below:</p>
+ %s
+ <p>Or copy and paste this link into your browser:</p>
+ %s',
+ $request_name,
+ $this->button($confirm_url, 'Confirm'),
+ $this->link($confirm_url)
+ );
+ $message = apply_filters('jvbPersonalDataExport', $message, $request_type, $confirm_url, $email_data);
+
+ $message .= $this->divider();
$message .= '<p>If you did not make this request, you can safely ignore this email.</p>';
- $message .= $this->signature;
return $this->getEmailTemplate($message, 'Action Confirmation');
}
@@ -571,23 +579,304 @@
*/
public function customizePersonalDataEmail(string $content, int $request_id, array $email_data):string
{
+ $download_url = $email_data['export_file_url'];
+ $expiresAt = $email_data['expiration_date'];
- // Extract the download link
- $pattern = '/<a href="([^"]+)"[^>]*>/';
- preg_match($pattern, $content, $matches);
- $download_url = isset($matches[1]) ? $matches[1] : '';
-
- $message = '<p>Your request for an export of personal data has been completed.</p>';
- $message .= '<p>You can download your personal data by clicking the button below.</p>';
- $message .= '<p style="text-align: center;"><a href="' . $download_url . '" class="button">Download Personal Data</a></p>';
- $message .= '<p>Or copy and paste this link into your browser:</p>';
- $message .= '<p style="user-select:all;">' . $download_url . '</p>';
- $message .= '<div class="divider"></div>';
- $message .= '<p><strong>Important:</strong> For privacy reasons, this link will expire in ' . $email_data['expiration'] . '.</p>';
- $message .= $this->signature;
+ $message = sprintf(
+ '<p>Hi,</p>
+ <p>You\'re receiving this email because you requested an export of your personal data.</p>
+ <p>You can download your personal data by clicking the button below:</p>
+ %s
+ <p>Or you can copy and paste this link into your browser:</p>
+ %s
+ %s
+ <p><strong>Important:</strong> For privacy and security, this link will expire at %s.</p>',
+ $this->button($download_url, 'Download Your Data'),
+ $this->link($download_url),
+ $this->divider(),
+ $expiresAt
+ );
+ $message = apply_filters('jvbPersonalDataExported', $message, $download_url, $expiresAt, $email_data);
return $this->getEmailTemplate($message, 'Your Personal Data Export');
}
+
+ public function signature():string
+ {
+ return '<p><i>'.$this->signature.'</i></p>';
+ }
+
+ public function button(string $link, string $title):string
+ {
+ return sprintf(
+ '<p style="text-align: center;"><a href="%s" style="display:inline-block;padding:16px 10px;border-radius:4px;background-color:%s;color:%s;text-decoration:none;font-weight:bold;margin:15px 0;letter-spacing:1px;text-transform:uppercase;">%s</a></p>',
+ $link,
+ $this->colours['action-0'],
+ $this->colours['action-contrast'],
+ $title
+ );
+ }
+
+ public function link(string $link):string
+ {
+ return sprintf(
+ '<code style="color:%s;border:1px solid %s;background-color:%s;border-radius:4px;user-select:all;">%s</code>',
+ $this->colours['dark-200'],
+ $this->colours['dark-100'],
+ $this->colours['light-100'],
+ $link
+ );
+ }
+
+ public function divider():string
+ {
+ return '<div style="border-top:1px solid '.$this->colours['dark-200'].';margin:25px 0;"></div>';
+ }
+
+ public function notice(string $text):string
+ {
+ return '<div style="border-radius: 0 8px 8px 0; margin: 1rem 0; border-left: 4px solid '.$this->colours['action-0'].';padding:1rem;background-color:'.$this->colours['light-100'].';">
+ '.str_replace('<strong>', '<strong style="color:'.$this->colours['action-0'].'">',$text).'
+ </div>';
+ }
+
+ public function callout(string $text):string
+ {
+ return sprintf(
+ '<div style="padding:2rem;margin:2rem 3rem;background-color:%s;color:%s;">%s</div>',
+ $this->colours['action-0'],
+ $this->colours['action-contrast'],
+ str_replace('<a', '<a style="background-color:'.$this->colours['action-contrast'].';padding: 0 .125rem;border-radius:4px;"', $text)
+ );
+ }
+
+ public function table(array $summarize, string $title = '', array $actions = []):string
+ {
+ if (empty($summarize)){
+ return '';
+ }
+
+ if (!empty($title)) {
+ $title = sprintf(
+ '<h2 style="color:%s;border-bottom:2px solid %s;padding-bottom:15px;margin-bottom:20px;text-align:center;">%s</h2>',
+ $this->colours['dark-200'],
+ $this->colours['dark-200'],
+ $title
+ );
+ }
+ $content = '';
+ foreach ($summarize as $index=> $item) {
+ if (!array_key_exists('label', $item) && !array_key_exists('value', $item)) {
+ continue;
+ }
+ $content .= sprintf(
+ '<div style="padding:10px 0;border-bottom:1px solid %s;background-color:%s;">
+ <span style="display:inline-block;vertical-align:top;font-weight:600;color:%s;width:%s;">%s</span>
+ <div style="display:inline-block;vertical-align:top;width:%s;">%s</div>
+ </div>',
+ $this->colours['dark-200'],
+ ($index%2 === 0) ? $this->colours['light-100'] : $this->colours['light-50'],
+ $this->colours['dark-200'],
+ '19%',
+ $item['label'],
+ '80%',
+ $item['value']
+ );
+ }
+
+ return sprintf(
+ '<div style="max-width:500px;margin:40px auto;padding:30px;background-color:%s;border-radius:10px;border:2px dashed %s;">%s%s</div>',
+ $this->colours['light-100'],
+ $this->colours['dark-200'],
+ $title,
+ $content
+ );
+ }
+
+ public function card(string $content, string $title = ''):string
+ {
+ $titleHtml = '';
+ if (!empty($title)) {
+ $titleHtml = sprintf(
+ '<h3 style="margin:0 0 15px 0;color:%s;font-size:18px;font-weight:600;">%s</h3>',
+ $this->colours['dark'],
+ esc_html($title)
+ );
+ }
+
+ return sprintf(
+ '<div style="background-color:%s;border:1px solid %s;border-radius:8px;padding:20px;margin:15px 0;">%s%s</div>',
+ $this->colours['light-50'],
+ $this->colours['light-200'],
+ $titleHtml,
+ $content
+ );
+ }
+
+ public function stat(string $number, string $label, string $description = ''):string
+ {
+ $desc = $description ? sprintf(
+ '<p style="margin:5px 0 0 0;font-size:12px;color:%s;">%s</p>',
+ $this->colours['dark-200'],
+ esc_html($description)
+ ) : '';
+
+ return sprintf(
+ '<div style="text-align:center;padding:20px;background-color:%s;border-radius:8px;margin:10px 0;">
+ <div style="font-size:36px;font-weight:700;color:%s;margin:0 0 5px 0;">%s</div>
+ <div style="font-size:14px;font-weight:600;color:%s;text-transform:uppercase;letter-spacing:1px;">%s</div>
+ %s
+ </div>',
+ $this->colours['light-100'],
+ $this->colours['action-0'],
+ esc_html($number),
+ $this->colours['dark-200'],
+ esc_html($label),
+ $desc
+ );
+ }
+
+ public function grid(array $items, int $columns = 2):string
+ {
+ $width = floor(100 / $columns) - 2; // 2% gap
+
+ $html = '<div style="display:table;width:100%;margin:20px 0;">';
+ foreach ($items as $index => $item) {
+ if ($index > 0 && $index % $columns === 0) {
+ $html .= '</div><div style="display:table;width:100%;margin:20px 0;">';
+ }
+ $html .= sprintf(
+ '<div style="display:table-cell;width:%d%%;padding:10px;vertical-align:top;">%s</div>',
+ $width,
+ $item
+ );
+ }
+ $html .= '</div>';
+
+ return $html;
+ }
+
+ public function image(string $src, string $alt = '', int $maxWidth = 600):string
+ {
+ return sprintf(
+ '<div style="text-align:center;margin:20px 0;">
+ <img src="%s" alt="%s" style="max-width:%dpx;width:100%%;height:auto;border-radius:8px;" />
+ </div>',
+ esc_url($src),
+ esc_attr($alt),
+ $maxWidth
+ );
+ }
+
+ public function h1(string $text):string
+ {
+ return sprintf(
+ '<h1 style="color:%s;font-size:24px;font-weight:700;margin:20px 0 10px 0;">%s</h1>',
+ $this->colours['dark'],
+ $text
+ );
+ }
+
+ public function h2(string $text):string
+ {
+ return sprintf(
+ '<h2 style="color:%s;font-size:20px;font-weight:600;margin:20px 0 10px 0;border-bottom:2px solid %s;padding-bottom:10px;">%s</h2>',
+ $this->colours['dark'],
+ $this->colours['dark-200'],
+ $text
+ );
+ }
+
+ public function h3(string $text):string
+ {
+ return sprintf(
+ '<h3 style="color:%s;font-size:18px;font-weight:600;margin:15px 0 10px 0;">%s</h3>',
+ $this->colours['dark-200'],
+ $text
+ );
+ }
+
+ public function list(array $items, bool $ordered = false):string
+ {
+ $tag = $ordered ? 'ol' : 'ul';
+ $style = $ordered
+ ? 'list-style-type:decimal;padding-left:20px;margin:10px 0;'
+ : 'list-style-type:disc;padding-left:20px;margin:10px 0;';
+
+ $html = sprintf('<%s style="%s">', $tag, $style);
+ foreach ($items as $item) {
+ $html .= sprintf(
+ '<li style="margin:5px 0;line-height:1.6;">%s</li>',
+ $item
+ );
+ }
+ $html .= sprintf('</%s>', $tag);
+
+ return $html;
+ }
+
+ public function codeBlock(string $code, string $language = ''):string
+ {
+ return sprintf(
+ '<pre style="background-color:%s;border:1px solid %s;border-radius:4px;padding:15px;overflow-x:auto;font-family:\'Courier New\',monospace;font-size:13px;line-height:1.4;"><code>%s</code></pre>',
+ $this->colours['light-100'],
+ $this->colours['dark-200'],
+ esc_html($code)
+ );
+ }
+
+ public function badge(string $text, string $type = 'default'):string
+ {
+ $colors = [
+ 'success' => ['bg' => '#d4edda', 'text' => '#155724'],
+ 'warning' => ['bg' => '#fff3cd', 'text' => '#856404'],
+ 'error' => ['bg' => '#f8d7da', 'text' => '#721c24'],
+ 'info' => ['bg' => '#d1ecf1', 'text' => '#0c5460'],
+ 'default' => ['bg' => $this->colours['light-200'], 'text' => $this->colours['dark-200']]
+ ];
+
+ $color = $colors[$type] ?? $colors['default'];
+
+ return sprintf(
+ '<span style="display:inline-block;padding:4px 12px;border-radius:12px;background-color:%s;color:%s;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;">%s</span>',
+ $color['bg'],
+ $color['text'],
+ esc_html($text)
+ );
+ }
+
+ public function alert(string $text, string $type = 'info'):string
+ {
+ $configs = [
+ 'success' => ['bg' => '#d4edda', 'border' => '#28a745', 'text' => '#155724', 'icon' => '✓'],
+ 'warning' => ['bg' => '#fff3cd', 'border' => '#ffc107', 'text' => '#856404', 'icon' => '⚠'],
+ 'error' => ['bg' => '#f8d7da', 'border' => '#dc3545', 'text' => '#721c24', 'icon' => '✕'],
+ 'info' => ['bg' => '#d1ecf1', 'border' => $this->colours['action-0'], 'text' => '#0c5460', 'icon' => 'ℹ']
+ ];
+
+ $config = $configs[$type] ?? $configs['info'];
+
+ return sprintf(
+ '<div style="border-radius:8px;margin:1rem 0;border-left:4px solid %s;padding:1rem;background-color:%s;color:%s;">
+ <strong style="font-size:16px;">%s</strong> %s
+ </div>',
+ $config['border'],
+ $config['bg'],
+ $config['text'],
+ $config['icon'],
+ $text
+ );
+ }
+
+ public function spacer(int $height = 20):string
+ {
+ return sprintf('<div style="height:%dpx;"></div>', $height);
+ }
+
+ public function prefix():string
+ {
+ return $this->prefix;
+ }
}
-new EmailManager();
+
--
Gitblit v1.10.0