Skip to content

Commit 40a3fce

Browse files
committed
Implement WP_Mailer class and refactor core emails (Ticket #49661)
1 parent e12ddb3 commit 40a3fce

6 files changed

Lines changed: 519 additions & 233 deletions

File tree

src/wp-admin/includes/privacy-tools.php

Lines changed: 33 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -632,112 +632,52 @@ function wp_privacy_send_personal_data_export_email( $request_id ) {
632632
'siteurl' => $site_url,
633633
);
634634

635-
/* translators: Personal data export notification email subject. %s: Site title. */
636-
$subject = sprintf( __( '[%s] Personal Data Export' ), $site_name );
637-
638-
/**
639-
* Filters the subject of the email sent when an export request is completed.
640-
*
641-
* @since 5.3.0
642-
*
643-
* @param string $subject The email subject.
644-
* @param string $sitename The name of the site.
645-
* @param array $email_data {
646-
* Data relating to the account action email.
647-
*
648-
* @type WP_User_Request $request User request object.
649-
* @type int $expiration The time in seconds until the export file expires.
650-
* @type string $expiration_date The localized date and time when the export file expires.
651-
* @type string $message_recipient The address that the email will be sent to. Defaults
652-
* to the value of `$request->email`, but can be changed
653-
* by the `wp_privacy_personal_data_email_to` filter.
654-
* @type string $export_file_url The export file URL.
655-
* @type string $sitename The site name sending the mail.
656-
* @type string $siteurl The site URL sending the mail.
657-
* }
658-
*/
659-
$subject = apply_filters( 'wp_privacy_personal_data_email_subject', $subject, $site_name, $email_data );
635+
$email_data = array(
636+
'request' => $request,
637+
'expiration' => $expiration,
638+
'expiration_date' => $expiration_date,
639+
'export_file_url' => $export_file_url,
640+
'sitename' => $site_name,
641+
'siteurl' => $site_url,
642+
);
660643

661-
/* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */
662-
$email_text = __(
663-
'Howdy,
644+
WP_Mailer::register_email(
645+
'privacy_export',
646+
'privacy',
647+
array(
648+
/* translators: Personal data export notification email subject. %s: Site title. */
649+
'subject' => __( '[{{sitename}}] Personal Data Export' ),
650+
/* translators: Do not translate {{expiration_date}}, {{export_file_url}}, {{sitename}}, {{siteurl}}: those are placeholders. */
651+
'body' => __(
652+
'Howdy,
664653
665654
Your request for an export of personal data has been completed. You may
666655
download your personal data by clicking on the link below. For privacy
667-
and security, we will automatically delete the file on ###EXPIRATION###,
656+
and security, we will automatically delete the file on {{expiration_date}},
668657
so please download it before then.
669658
670-
###LINK###
659+
{{export_file_url}}
671660
672661
Regards,
673-
All at ###SITENAME###
674-
###SITEURL###'
662+
All at {{sitename}}
663+
{{siteurl}}'
664+
),
665+
)
675666
);
676667

677-
/**
678-
* Filters the text of the email sent with a personal data export file.
679-
*
680-
* The following strings have a special meaning and will get replaced dynamically:
681-
*
682-
* - `###EXPIRATION###` The date when the URL will be automatically deleted.
683-
* - `###LINK###` URL of the personal data export file for the user.
684-
* - `###SITENAME###` The name of the site.
685-
* - `###SITEURL###` The URL to the site.
686-
*
687-
* @since 4.9.6
688-
* @since 5.3.0 Introduced the `$email_data` array.
689-
*
690-
* @param string $email_text Text in the email.
691-
* @param int $request_id The request ID for this personal data export.
692-
* @param array $email_data {
693-
* Data relating to the account action email.
694-
*
695-
* @type WP_User_Request $request User request object.
696-
* @type int $expiration The time in seconds until the export file expires.
697-
* @type string $expiration_date The localized date and time when the export file expires.
698-
* @type string $message_recipient The address that the email will be sent to. Defaults
699-
* to the value of `$request->email`, but can be changed
700-
* by the `wp_privacy_personal_data_email_to` filter.
701-
* @type string $export_file_url The export file URL.
702-
* @type string $sitename The site name sending the mail.
703-
* @type string $siteurl The site URL sending the mail.
704-
*/
705-
$content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id, $email_data );
706-
707-
$content = str_replace( '###EXPIRATION###', $expiration_date, $content );
708-
$content = str_replace( '###LINK###', sanitize_url( $export_file_url ), $content );
709-
$content = str_replace( '###EMAIL###', $request_email, $content );
710-
$content = str_replace( '###SITENAME###', $site_name, $content );
711-
$content = str_replace( '###SITEURL###', sanitize_url( $site_url ), $content );
712-
713668
$headers = '';
714669

715-
/**
716-
* Filters the headers of the email sent with a personal data export file.
717-
*
718-
* @since 5.4.0
719-
*
720-
* @param string|array $headers The email headers.
721-
* @param string $subject The email subject.
722-
* @param string $content The email content.
723-
* @param int $request_id The request ID.
724-
* @param array $email_data {
725-
* Data relating to the account action email.
726-
*
727-
* @type WP_User_Request $request User request object.
728-
* @type int $expiration The time in seconds until the export file expires.
729-
* @type string $expiration_date The localized date and time when the export file expires.
730-
* @type string $message_recipient The address that the email will be sent to. Defaults
731-
* to the value of `$request->email`, but can be changed
732-
* by the `wp_privacy_personal_data_email_to` filter.
733-
* @type string $export_file_url The export file URL.
734-
* @type string $sitename The site name sending the mail.
735-
* @type string $siteurl The site URL sending the mail.
736-
* }
737-
*/
738-
$headers = apply_filters( 'wp_privacy_personal_data_email_headers', $headers, $subject, $content, $request_id, $email_data );
670+
/** This filter is documented in wp-admin/includes/privacy-tools.php */
671+
$headers = apply_filters( 'wp_privacy_personal_data_email_headers', $headers, '', '', $request_id, $email_data );
739672

740-
$mail_success = wp_mail( $request_email, $subject, $content, $headers );
673+
$mail_success = WP_Mailer::send(
674+
'privacy_export',
675+
array(
676+
'to' => $request_email,
677+
'headers' => $headers,
678+
),
679+
$email_data
680+
);
741681

742682
if ( $switched_locale ) {
743683
restore_previous_locale();
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
<?php
2+
/**
3+
* WP_Mailer class.
4+
*
5+
* A standard way to manage emails sent by WordPress core.
6+
*
7+
* @package WordPress
8+
* @subpackage Mail
9+
* @since 7.1.0
10+
*/
11+
12+
/**
13+
* Core class used to send standardized emails.
14+
*
15+
* @since 7.1.0
16+
*/
17+
class WP_Mailer {
18+
19+
/**
20+
* Registered emails.
21+
*
22+
* @since 7.1.0
23+
* @var array
24+
*/
25+
private static $emails = array();
26+
27+
/**
28+
* Sends a standardized email.
29+
*
30+
* @since 7.1.0
31+
*
32+
* @param string $email_id Email identifier.
33+
* @param array $args {
34+
* Optional. Email arguments.
35+
*
36+
* @type string|string[] $to Array or comma-separated list of email addresses.
37+
* @type string|string[] $headers Optional. Additional headers.
38+
* @type string|string[] $attachments Optional. Paths to files to attach.
39+
* }
40+
* @param array $data Optional. Data to be used in the template placeholders.
41+
* @return bool Whether the email was sent successfully.
42+
*/
43+
public static function send( $email_id, $args = array(), $data = array() ) {
44+
$email = self::get_email( $email_id );
45+
46+
if ( ! $email ) {
47+
return false;
48+
}
49+
50+
$group = $email['group'];
51+
52+
/**
53+
* Filters the data used for email template rendering.
54+
*
55+
* @since 7.1.0
56+
*
57+
* @param array $data The template data.
58+
* @param string $email_id The email identifier.
59+
* @param string $group The email group.
60+
*/
61+
$data = apply_filters( "wp_mailer_{$group}_data", $data, $email_id, $group );
62+
$data = apply_filters( "wp_mailer_{$email_id}_data", $data, $email_id, $group );
63+
64+
$subject = self::render( $email['subject'], $data );
65+
$message = self::render( $email['body'], $data );
66+
67+
/**
68+
* Filters the email subject before sending.
69+
*
70+
* @since 7.1.0
71+
*
72+
* @param string $subject The rendered subject.
73+
* @param string $email_id The email identifier.
74+
* @param array $data The template data.
75+
*/
76+
$subject = apply_filters( "wp_mailer_{$group}_subject", $subject, $email_id, $data );
77+
$subject = apply_filters( "wp_mailer_{$email_id}_subject", $subject, $email_id, $data );
78+
79+
/**
80+
* Filters the email message before sending.
81+
*
82+
* @since 7.1.0
83+
*
84+
* @param string $message The rendered message.
85+
* @param string $email_id The email identifier.
86+
* @param array $data The template data.
87+
*/
88+
$message = apply_filters( "wp_mailer_{$group}_message", $message, $email_id, $data );
89+
$message = apply_filters( "wp_mailer_{$email_id}_message", $message, $email_id, $data );
90+
91+
$to = isset( $args['to'] ) ? $args['to'] : '';
92+
$headers = isset( $args['headers'] ) ? $args['headers'] : '';
93+
$attachments = isset( $args['attachments'] ) ? $args['attachments'] : array();
94+
95+
/**
96+
* Filters the email headers before sending.
97+
*
98+
* @since 7.1.0
99+
*
100+
* @param string|array $headers The email headers.
101+
* @param string $email_id The email identifier.
102+
* @param array $data The template data.
103+
*/
104+
$headers = apply_filters( "wp_mailer_{$group}_headers", $headers, $email_id, $data );
105+
$headers = apply_filters( "wp_mailer_{$email_id}_headers", $headers, $email_id, $data );
106+
107+
return wp_mail( $to, $subject, $message, $headers, $attachments );
108+
}
109+
110+
/**
111+
* Registers an email template.
112+
*
113+
* @since 7.1.0
114+
*
115+
* @param string $email_id Email identifier.
116+
* @param string $group Email group (e.g., 'privacy', 'admin').
117+
* @param array $args {
118+
* Email template arguments.
119+
*
120+
* @type string $subject Email subject template.
121+
* @type string $body Email body template.
122+
* }
123+
*/
124+
public static function register_email( $email_id, $group, $args ) {
125+
self::$emails[ $email_id ] = array(
126+
'group' => $group,
127+
'subject' => $args['subject'],
128+
'body' => $args['body'],
129+
);
130+
}
131+
132+
/**
133+
* Retrieves an email template.
134+
*
135+
* @since 7.1.0
136+
*
137+
* @param string $email_id Email identifier.
138+
* @return array|false Email template data on success, false on failure.
139+
*/
140+
public static function get_email( $email_id ) {
141+
if ( isset( self::$emails[ $email_id ] ) ) {
142+
return self::$emails[ $email_id ];
143+
}
144+
145+
/**
146+
* Filters the email template before it is retrieved.
147+
*
148+
* This allows for lazy registration of emails.
149+
*
150+
* @since 7.1.0
151+
*
152+
* @param array|null $email The email template data.
153+
* @param string $email_id The email identifier.
154+
*/
155+
return apply_filters( "wp_mailer_get_email_{$email_id}", null, $email_id );
156+
}
157+
158+
/**
159+
* Renders a template with data.
160+
*
161+
* Supports {{mustache}} style placeholders.
162+
*
163+
* @since 7.1.0
164+
*
165+
* @param string $template The template string.
166+
* @param array $data The data for replacement.
167+
* @return string The rendered string.
168+
*/
169+
public static function render( $template, $data ) {
170+
return preg_replace_callback(
171+
'/{{(.*?)}}/',
172+
function ( $matches ) use ( $data ) {
173+
$key = trim( $matches[1] );
174+
return isset( $data[ $key ] ) ? $data[ $key ] : $matches[0];
175+
},
176+
$template
177+
);
178+
}
179+
}

0 commit comments

Comments
 (0)