PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.26
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.26
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / controllers / FrmEmailStylesController.php

FrmEmailStylesController.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.26, at classes/controllers/FrmEmailStylesController.php

506 lines 13.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Controller for email styles
4 *
5 * @since 6.25
6 *
7 * @package Formidable
8 */
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 die( 'You are not allowed to call this page directly.' );
12 }
13
14 /**
15 * Class FrmEmailStylesController
16 */
17 class FrmEmailStylesController {
18
19 /**
20 * Gets email styles.
21 *
22 * @return array[]
23 */
24 public static function get_email_styles() {
25 $icon_dir_url = FrmAppHelper::plugin_url() . '/images/email-styles/';
26
27 $email_styles = array(
28 'classic' => array(
29 'name' => __( 'Classic', 'formidable' ),
30 'selectable' => true,
31 'icon_url' => $icon_dir_url . 'classic.svg',
32 'is_plain_text' => false,
33 ),
34 'plain' => array(
35 'name' => __( 'Plain Text', 'formidable' ),
36 'selectable' => true,
37 'icon_url' => $icon_dir_url . 'plain.svg',
38 'is_plain_text' => true,
39 ),
40 'modern' => array(
41 'name' => __( 'Modern', 'formidable' ),
42 'selectable' => false,
43 'icon_url' => $icon_dir_url . 'modern.svg',
44 'is_plain_text' => false,
45 ),
46 'sleek' => array(
47 'name' => __( 'Sleek', 'formidable' ),
48 'selectable' => false,
49 'icon_url' => $icon_dir_url . 'sleek.svg',
50 'is_plain_text' => false,
51 ),
52 'compact' => array(
53 'name' => __( 'Compact', 'formidable' ),
54 'selectable' => false,
55 'icon_url' => $icon_dir_url . 'compact.svg',
56 'is_plain_text' => false,
57 ),
58 );
59
60 /**
61 * Filter the email styles.
62 *
63 * @since 6.25
64 *
65 * @param array[] $email_styles The email styles.
66 *
67 * @return array
68 */
69 return apply_filters( 'frm_email_styles', $email_styles );
70 }
71
72 /**
73 * Gets email style preview URL.
74 *
75 * @param string $style_key Style key.
76 *
77 * @return string
78 */
79 public static function get_email_style_preview_url( $style_key ) {
80 return wp_nonce_url( admin_url( 'admin-ajax.php?action=frm_email_style_preview&style_key=' . $style_key ), 'frm_email_style_preview' );
81 }
82
83 /**
84 * Gets the email style set in the Global settings.
85 *
86 * @return string
87 */
88 public static function get_default_email_style() {
89 $frm_settings = FrmAppHelper::get_settings();
90
91 if ( empty( $frm_settings->email_style ) ) {
92 return 'classic';
93 }
94
95 // Check if the selected style is available and selectable.
96 $styles = self::get_email_styles();
97 $style = $frm_settings->email_style;
98
99 if ( isset( $styles[ $style ] ) && ! empty( $styles[ $style ]['selectable'] ) ) {
100 return $style;
101 }
102
103 return 'classic';
104 }
105
106 /**
107 * Gets the test email content.
108 *
109 * @param false|string $style_key Default is `false`, using the one in settings.
110 *
111 * @return string
112 */
113 private static function get_test_email_content( $style_key = false ) {
114 if ( ! $style_key ) {
115 $style_key = self::get_default_email_style();
116 }
117
118 $table_rows = array(
119 array(
120 'label' => 'Name',
121 'value' => 'John Doe',
122 ),
123 array(
124 'label' => 'Email address',
125 'value' => 'john@doe.com',
126 ),
127 array(
128 'label' => 'Subject',
129 'value' => 'Contact subject',
130 ),
131 array(
132 'label' => 'Message',
133 'value' => 'Lorem ipsum dolor sit amet, <a href="#">consectetur adipiscing elit</a>.
134 Praesent in risus velit. Donec molestie tincidunt ex sed consequat. Ut ornare fringilla fringilla.',
135 ),
136 );
137
138 if ( 'plain' !== $style_key ) {
139 $content = self::get_test_rich_text_email_content( $style_key, $table_rows );
140 } else {
141 $content = '';
142
143 foreach ( $table_rows as $row ) {
144 $content .= $row['label'] . ': ' . $row['value'] . "\r\n";
145 }
146 }//end if
147
148 return $content;
149 }
150
151 /**
152 * Gets the test email content.
153 *
154 * @param string $style_key Style key.
155 * @param array $table_rows Table rows.
156 *
157 * @return string
158 */
159 private static function get_test_rich_text_email_content( $style_key, $table_rows ) {
160 $style_settings = self::get_email_style_settings();
161
162 // Sleek table style doesn't have any border.
163 $should_remove_border = 'sleek' === $style_key;
164
165 // Modern and Compact table styles don't have top and bottom border.
166 $should_remove_top_bottom_border = 'classic' !== $style_key;
167
168 $table_generator = self::get_table_generator( $style_key );
169
170 $content = $table_generator->generate_table_header();
171
172 // By default, table has the bottom border and table cells have top border.
173 if ( $should_remove_top_bottom_border ) {
174 $content = $table_generator->remove_border( $content, 'bottom' );
175 }
176
177 foreach ( $table_rows as $index => $row ) {
178 if ( 'compact' === $style_key ) {
179 // Add some spaces after the label. Maybe change it to the left in RTL.
180 $row['label'] = '<div style="padding-right:10px;">' . $row['label'] . '</div>';
181
182 // Compact table has two columns layout.
183 $table_row = $table_generator->generate_two_cell_table_row( $row['label'], $row['value'] );
184 } else {
185 // Other table styles have one column layout.
186 $table_row = $table_generator->generate_single_cell_table_row( self::get_content_for_one_column_cell( $row['label'], $row['value'] ) );
187 }
188
189 if ( ! $index && $should_remove_top_bottom_border ) {
190 $table_row = $table_generator->remove_border( $table_row );
191 }
192
193 $content .= $table_row;
194 }
195
196 $content .= $table_generator->generate_table_footer();
197
198 if ( $should_remove_border ) {
199 $content = $table_generator->remove_border( $content, 'top' );
200 }
201
202 if ( 'classic' !== $style_key ) {
203 $content = self::wrap_email_message( $content );
204 }
205
206 $wrapped_content = '<html><head><meta charset="utf-8" /></head>';
207
208 if ( 'classic' !== $style_key ) {
209 // This works in previewing and as a fallback for email content.
210 $wrapped_content .= '<style>
211 body {background-color:' . esc_attr( $style_settings['bg_color'] ) . ';}
212 a {color:' . esc_attr( $style_settings['link_color'] ) . ';}
213 </style>';
214 }
215
216 $wrapped_content .= '</head><body>' . $content . '</body></html>';
217
218 return $wrapped_content;
219 }
220
221 /**
222 * Gets content for the cell of one column table.
223 *
224 * @param string $label Field label.
225 * @param string $value Prepared field value.
226 *
227 * @return string
228 */
229 public static function get_content_for_one_column_cell( $label, $value ) {
230 return '<div style="font-weight:600;">' . $label . '</div>' . $value;
231 }
232
233 /**
234 * Gets table generator object for an email style.
235 *
236 * @param false|string $email_style Email style. Default is `false`: using the one in global settings.
237 *
238 * @return FrmTableHTMLGenerator
239 */
240 public static function get_table_generator( $email_style = false ) {
241 if ( false === $email_style ) {
242 $email_style = self::get_default_email_style();
243 }
244
245 $style_settings = self::get_email_style_settings();
246
247 $atts = array(
248 'inline_style' => true,
249 'email_style' => $email_style,
250 );
251
252 if ( 'classic' !== $email_style ) {
253 $atts['width'] = '100%';
254 $atts['border_color'] = $style_settings['border_color'];
255 $atts['cell_padding'] = '16px 0';
256 $atts['bg_color'] = $style_settings['container_bg_color'];
257 $atts['alt_bg_color'] = $style_settings['container_bg_color'];
258 $atts['text_color'] = $style_settings['text_color'];
259 }
260
261 return new FrmTableHTMLGenerator( 'entry', $atts );
262 }
263
264 /**
265 * AJAX handler for previewing email style.
266 */
267 public static function ajax_preview() {
268 // Check permission and nonce.
269 FrmAppHelper::permission_check( 'frm_change_settings' );
270 check_ajax_referer( 'frm_email_style_preview' );
271
272 $style_key = FrmAppHelper::get_param( 'style_key', '', 'get', 'sanitize_text_field' );
273 $not_exist_msg = __( "This email style doesn't exist", 'formidable' );
274
275 if ( ! $style_key ) {
276 die( esc_html( $not_exist_msg ) );
277 }
278
279 $styles = self::get_email_styles();
280
281 if ( ! isset( $styles[ $style_key ] ) ) {
282 die( esc_html( $not_exist_msg ) );
283 }
284
285 $style_key = FrmAppHelper::get_param( 'style_key', '', 'sanitize_text_field' );
286 $content = self::get_test_email_content( $style_key );
287
288 header( self::get_content_type_header( $style_key ) );
289
290 echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
291
292 die();
293 }
294
295 /**
296 * AJAX handler for sending a test email.
297 */
298 public static function ajax_send_test_email() {
299 // Check permission and nonce.
300 FrmAppHelper::permission_check( 'frm_change_settings' );
301 check_ajax_referer( 'frm_ajax', 'nonce' );
302
303 $emails_str = FrmAppHelper::get_post_param( 'emails_str', '', 'sanitize_text_field' );
304 $emails = explode( ',', $emails_str );
305 $valid_emails = array();
306
307 foreach ( $emails as $email ) {
308 $email = trim( $email );
309
310 if ( empty( $email ) || ! is_email( $email ) ) {
311 continue;
312 }
313 $valid_emails[] = $email;
314 }
315
316 if ( empty( $valid_emails ) ) {
317 wp_send_json_error( __( 'Invalid email address', 'formidable' ) );
318 }
319
320 $email_style = self::get_default_email_style();
321
322 $subject = __( 'Formidable Test Email', 'formidable' );
323 $content = self::get_test_email_content();
324 $headers = array(
325 self::get_content_type_header( $email_style ),
326 );
327
328 FrmUsageController::update_flows_data( 'send_test_email', $email_style );
329
330 $result = wp_mail( $valid_emails, $subject, $content, $headers );
331
332 if ( $result ) {
333 wp_send_json_success( __( 'Test email sent successfully!', 'formidable' ) );
334 }
335
336 wp_send_json_error( __( 'Failed to send test email!', 'formidable' ) );
337 }
338
339 /**
340 * Gets Content-Type header.
341 *
342 * @param string $email_style Email style.
343 *
344 * @return string
345 */
346 private static function get_content_type_header( $email_style ) {
347 $content_type = 'plain' === $email_style ? 'text/plain' : 'text/html';
348 return 'Content-Type: ' . $content_type . '; charset=utf-8';
349 }
350
351 /**
352 * Shows placeholder Pro settings.
353 *
354 * @return void
355 */
356 public static function show_upsell_settings() {
357 include FrmAppHelper::plugin_path() . '/classes/views/frm-settings/email/settings.php';
358 }
359
360 /**
361 * Gets email style settings value.
362 *
363 * @return array
364 */
365 public static function get_email_style_settings() {
366 /**
367 * Filter the email style settings value.
368 *
369 * @since 6.25
370 *
371 * @param array $settings The settings value.
372 */
373 return apply_filters(
374 'frm_email_style_settings',
375 array(
376 // Placeholder image.
377 'img' => FrmAppHelper::plugin_url() . '/images/email-styles/placeholder.png',
378 'img_size' => '',
379 'img_align' => '',
380 'img_location' => '',
381 'bg_color' => '#EAECF0',
382 'container_bg_color' => '#ffffff',
383 'text_color' => '#475467',
384 'link_color' => '#4199FD',
385 'border_color' => '#dddddd',
386 'font' => '',
387 )
388 );
389 }
390
391 /**
392 * Wraps email message with new settings.
393 *
394 * @param string $message Email message.
395 *
396 * @return string
397 */
398 public static function wrap_email_message( $message ) {
399 $style_settings = self::get_email_style_settings();
400
401 $header_img = '';
402
403 if ( $style_settings['img'] ) {
404 $img_align = $style_settings['img_align'] ? $style_settings['img_align'] : 'center';
405 $img_size = $style_settings['img_size'] ? $style_settings['img_size'] : 'thumbnail';
406 $img_url = is_numeric( $style_settings['img'] ) ? wp_get_attachment_image_url( $style_settings['img'], $img_size ) : $style_settings['img'];
407
408 $header_img .= sprintf(
409 '<div style="text-align:%s;margin-bottom:32px;">',
410 esc_attr( $img_align )
411 );
412
413 $header_img .= sprintf(
414 '<img src="%s" alt="" style="max-width:100%%;height:auto;" />',
415 esc_url( $img_url )
416 );
417
418 $header_img .= '</div>';
419 }
420
421 // Wrapper.
422 $font_family = $style_settings['font'] ? $style_settings['font'] : 'Inter,sans-serif';
423 $new_message = sprintf(
424 '<div style="background-color:%1$s;color:%2$s;font-family:%3$s;padding:40px 10px;">',
425 esc_attr( $style_settings['bg_color'] ),
426 esc_attr( $style_settings['text_color'] ),
427 esc_attr( $font_family )
428 );
429
430 // Container.
431 $new_message .= '<div style="max-width:640px;margin:auto;">';
432
433 // Header image if outside.
434 if ( $style_settings['img'] && 'inside' !== $style_settings['img_location'] ) {
435 $new_message .= $header_img;
436 }
437
438 // Main container.
439 $new_message .= sprintf(
440 '<div style="background-color:%s;border-radius:8px;padding:32px;">',
441 esc_attr( $style_settings['container_bg_color'] )
442 );
443
444 // Header image if inside.
445 if ( $style_settings['img'] && 'inside' === $style_settings['img_location'] ) {
446 $new_message .= $header_img;
447 }
448
449 // The message.
450 $new_message .= self::add_inline_css( 'a', 'color:' . $style_settings['link_color'] . ';', $message );
451
452 $new_message .= '</div></div></div>';
453
454 return $new_message;
455 }
456
457 /**
458 * Adds inline CSS to a tag in the content.
459 *
460 * @param string $tag Tag name.
461 * @param string $css CSS code.
462 * @param string $content The content.
463 *
464 * @return string
465 */
466 private static function add_inline_css( $tag, $css, $content ) {
467 $regex = '/<' . $tag . '.*?>/msi';
468 preg_match_all( $regex, $content, $matches, PREG_SET_ORDER );
469
470 if ( ! $matches ) {
471 return $content;
472 }
473
474 $searches = array();
475 $replaces = array();
476
477 foreach ( $matches as $match ) {
478 $searches[] = $match[0];
479 $replaces[] = self::add_css_to_style_attr( $tag, $css, $match[0] );
480 }
481
482 return str_replace( $searches, $replaces, $content );
483 }
484
485 /**
486 * Adds inline CSS to a single HTML tag.
487 *
488 * @param string $tag Tag name.
489 * @param string $css CSS code.
490 * @param string $html The HTML tag.
491 *
492 * @return string
493 */
494 private static function add_css_to_style_attr( $tag, $css, $html ) {
495 $regex = '/\sstyle=("|\')/mi';
496
497 if ( preg_match( $regex, $html, $matches ) ) {
498 $search = $matches[0];
499 $replace = $search . $css;
500 return preg_replace( $regex, $replace, $html );
501 }
502
503 return str_replace( '<' . $tag, '<' . $tag . ' style="' . $css . '"', $html );
504 }
505 }
506