PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.2.1
Kirki – Freeform Page Builder, Website Builder & Customizer v6.2.1
6.2.1 6.2.0 6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / app / FormActions / Actions / EmailActionHandler.php
kirki / app / FormActions / Actions Last commit date
EmailActionHandler.php 3 days ago WebhookActionHandler.php 3 days ago
EmailActionHandler.php
240 lines
1 <?php
2
3 namespace Kirki\App\FormActions\Actions;
4
5 defined('ABSPATH') || exit;
6
7 use Kirki\App\Constants\Form\FormEmailBodyPartTypes;
8 use Kirki\App\Contracts\FormActionHandler;
9 use Kirki\App\DTO\Form\FormConfigDTO;
10
11 /**
12 * Sends a form submission over email, for a single configured `email` action.
13 */
14 class EmailActionHandler implements FormActionHandler
15 {
16 public function handle(array $action, array $form_data, FormConfigDTO $form_config)
17 {
18 $this->register_shortcodes($action, $form_data); //@todo: maybe we can implement this without wp shortcodes but a different approach as current implementation might collide with other shortcodes registered
19
20 return $this->send_single_email_action($action, $form_data, $form_config->name);
21 }
22
23 /**
24 * Register shortcodes referenced by the email action's fields.
25 *
26 * Every field is rendered through {@see render_email_action_text()}, which
27 * strips any shortcode that does not map to a submitted form field, so no
28 * arbitrary site shortcode can execute here.
29 *
30 * @param array $action
31 * @param array $form_data
32 * @return void
33 */
34 protected function register_shortcodes($action, $form_data)
35 {
36 $this->register_shortcodes_from_field($action['emailList'] ?? '', $form_data);
37 $this->register_shortcodes_from_field($action['replyTo'] ?? '', $form_data);
38 $this->register_shortcodes_from_field($action['name'] ?? '', $form_data);
39 $this->register_shortcodes_from_field($action['subject'] ?? '', $form_data);
40
41 add_shortcode(
42 'admin_email',
43 function () {
44 return get_option('admin_email');
45 }
46 );
47 }
48
49 /**
50 * Register shortcodes referenced in a single email action field.
51 *
52 * @param string $field_value The field value containing shortcodes.
53 * @param array $form_data The form data to use for shortcode values.
54 * @return void
55 */
56 protected function register_shortcodes_from_field($field_value, $form_data)
57 {
58 if (empty($field_value)) {
59 return;
60 }
61
62 $regex = '/\[([^\]]+)\]/';
63 preg_match_all($regex, $field_value, $matches);
64
65 foreach (($matches[1] ?? []) as $match) {
66 if (isset($form_data[$match])) {
67 add_shortcode($match, fn() => $form_data[$match]);
68 }
69 }
70 }
71
72 /**
73 * Send a single email action.
74 *
75 * @param array $email_action Email action configuration.
76 * @param array $form_data Form data.
77 * @param string $form_name Form name.
78 * @return bool
79 */
80 protected function send_single_email_action($email_action, $form_data, $form_name)
81 {
82 $body = $this->convert_form_data_into_html_for_email($form_data);
83 $reply_to = '';
84 $name = '';
85 $subject = 'New ' . $form_name;
86 $header = [];
87
88 if (isset($email_action['body']) && is_array($email_action['body'])) {
89 $body = $this->build_email_body($email_action['body'], $form_data);
90 }
91
92 if (isset($email_action['replyTo'])) {
93 $reply_to = sanitize_email($this->render_email_action_text($email_action['replyTo'], $form_data));
94 }
95
96 if (isset($email_action['name'])) {
97 $name = $this->render_email_action_text($email_action['name'], $form_data);
98 }
99
100 if (isset($email_action['subject'])) {
101 $subject = $this->render_email_action_text($email_action['subject'], $form_data);
102 }
103
104 if (strlen($reply_to) > 0 && strlen($name) > 0) {
105 $header = ['Reply-To: ' . $name . ' <' . $reply_to . '>'];
106 }
107
108 if (isset($email_action['emailList']) && !empty($email_action['emailList'])) {
109 $to = $this->sanitize_email_list($this->render_email_action_text($email_action['emailList'], $form_data));
110
111 if ($to) {
112 return $this->send_email_notification($to, $subject, $body, $header);
113 }
114 }
115
116 return false;
117 }
118
119 /**
120 * Render a templated email action field using an explicit whitelist array.
121 *
122 * @param string $value The configured field value.
123 * @param array $form_data The submitted form data.
124 * @return string
125 */
126 protected function render_email_action_text($value, $form_data)
127 {
128 if (empty($value) || !is_string($value)) {
129 return (string) $value;
130 }
131
132 // Whitelist array of acceptable email action shortcodes and their resolved values.
133 $whitelist = [
134 'admin_email' => (string) get_option('admin_email'),
135 ];
136
137 if (is_array($form_data)) {
138 foreach ($form_data as $field_name => $field_value) {
139 if (is_array($field_value)) {
140 $whitelist[$field_name] = implode(', ', $field_value);
141 } else {
142 $whitelist[$field_name] = (string) $field_value;
143 }
144 }
145 }
146
147 // Render only the whitelisted shortcodes from the array
148 foreach ($whitelist as $tag => $replacement) {
149 $value = str_replace('[' . $tag . ']', $replacement, $value);
150 }
151
152 return $value;
153 }
154
155 /**
156 * Sanitize a comma-separated list of email addresses.
157 *
158 * @param string $value The configured email list value.
159 * @return string A comma-separated string of valid addresses, or '' when empty.
160 */
161 protected function sanitize_email_list($value)
162 {
163 if (empty($value) || !is_string($value)) {
164 return '';
165 }
166
167 $valid = [];
168
169 foreach (explode(',', $value) as $address) {
170 $address = sanitize_email(trim($address));
171
172 if ($address) {
173 $valid[] = $address;
174 }
175 }
176
177 return implode(', ', $valid);
178 }
179
180 /**
181 * Build the email body from a body configuration.
182 *
183 * @param array $body_config Body configuration.
184 * @param array $form_data Form data.
185 * @return string Email body HTML.
186 */
187 protected function build_email_body($body_config, $form_data)
188 {
189 $body_parts = [];
190
191 foreach ($body_config as $body_data) {
192 if (!isset($body_data['type'], $body_data['value'])) {
193 continue;
194 }
195
196 if ($body_data['type'] === FormEmailBodyPartTypes::TEXT) {
197 $body_parts[] = $body_data['value'];
198 } elseif ($body_data['type'] === FormEmailBodyPartTypes::FORM && isset($form_data[$body_data['value']])) {
199 $body_parts[] = $form_data[$body_data['value']];
200 }
201 }
202 return nl2br(implode('', $body_parts));
203 }
204
205 /**
206 * Convert form data into an HTML list for email.
207 *
208 * @param array $form_data Form data.
209 * @return string
210 */
211 protected function convert_form_data_into_html_for_email($form_data = [])
212 {
213 $html = '<ul>';
214
215 if (is_array($form_data)) {
216 foreach ($form_data as $key => $value) {
217 $html .= '<li>' . esc_html($key) . ': ' . esc_html($value) . '</li>';
218 }
219 }
220
221 $html .= '</ul>';
222 return $html;
223 }
224
225 /**
226 * Send an email notification.
227 *
228 * @param string|string[] $to Address(es) to send to.
229 * @param string $subject Email subject.
230 * @param string $message Message contents.
231 * @param array $headers Email headers.
232 * @return bool
233 */
234 protected function send_email_notification($to, $subject, $message, $headers = [])
235 {
236 $headers[] = 'Content-Type: text/html; charset=UTF-8';
237 return wp_mail($to, $subject, $message, $headers);
238 }
239 }
240