PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.35
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.35
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.35, at classes/controllers/FrmEmailStylesController.php

497 lines 13.4 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 = '';
140
141 foreach ( $table_rows as $row ) {
142 $content .= $row['label'] . ': ' . $row['value'] . "\r\n";
143 }
144 } else {
145 $content = self::get_test_rich_text_email_content( $style_key, $table_rows );
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 $table_generator = self::get_table_generator( $style_key );
168 $content = $table_generator->generate_table_header();
169
170 // By default, table has the bottom border and table cells have top border.
171 if ( $should_remove_top_bottom_border ) {
172 $content = $table_generator->remove_border( $content, 'bottom' );
173 }
174
175 foreach ( $table_rows as $index => $row ) {
176 if ( 'compact' === $style_key ) {
177 // Add some spaces after the label. Maybe change it to the left in RTL.
178 $row['label'] = '<div style="padding-right:10px;">' . $row['label'] . '</div>';
179
180 // Compact table has two columns layout.
181 $table_row = $table_generator->generate_two_cell_table_row( $row['label'], $row['value'] );
182 } else {
183 // Other table styles have one column layout.
184 $table_row = $table_generator->generate_single_cell_table_row( self::get_content_for_one_column_cell( $row['label'], $row['value'] ) );
185 }
186
187 if ( ! $index && $should_remove_top_bottom_border ) {
188 $table_row = $table_generator->remove_border( $table_row );
189 }
190
191 $content .= $table_row;
192 }
193
194 $content .= $table_generator->generate_table_footer();
195
196 if ( $should_remove_border ) {
197 $content = $table_generator->remove_border( $content, 'top' );
198 }
199
200 if ( 'classic' !== $style_key ) {
201 $content = self::wrap_email_message( $content );
202 }
203
204 $wrapped_content = '<html><head><meta charset="utf-8" /></head>';
205
206 if ( 'classic' !== $style_key ) {
207 // This works in previewing and as a fallback for email content.
208 $wrapped_content .= '<style>
209 body {background-color:' . esc_attr( $style_settings['bg_color'] ) . ';}
210 a {color:' . esc_attr( $style_settings['link_color'] ) . ';}
211 </style>';
212 }
213
214 return $wrapped_content . ( '</head><body>' . $content . '</body></html>' );
215 }
216
217 /**
218 * Gets content for the cell of one column table.
219 *
220 * @param string $label Field label.
221 * @param string $value Prepared field value.
222 *
223 * @return string
224 */
225 public static function get_content_for_one_column_cell( $label, $value ) {
226 return '<div style="font-weight:600;">' . $label . '</div>' . $value;
227 }
228
229 /**
230 * Gets table generator object for an email style.
231 *
232 * @param false|string $email_style Email style. Default is `false`: using the one in global settings.
233 *
234 * @return FrmTableHTMLGenerator
235 */
236 public static function get_table_generator( $email_style = false ) {
237 if ( false === $email_style ) {
238 $email_style = self::get_default_email_style();
239 }
240
241 $style_settings = self::get_email_style_settings();
242
243 $atts = array(
244 'inline_style' => true,
245 'email_style' => $email_style,
246 );
247
248 if ( 'classic' !== $email_style ) {
249 $atts['width'] = '100%';
250 $atts['border_color'] = $style_settings['border_color'];
251 $atts['cell_padding'] = '16px 0';
252 $atts['bg_color'] = $style_settings['container_bg_color'];
253 $atts['alt_bg_color'] = $style_settings['container_bg_color'];
254 $atts['text_color'] = $style_settings['text_color'];
255 }
256
257 return new FrmTableHTMLGenerator( 'entry', $atts );
258 }
259
260 /**
261 * AJAX handler for previewing email style.
262 */
263 public static function ajax_preview() {
264 // Check permission and nonce.
265 FrmAppHelper::permission_check( 'frm_change_settings' );
266 check_ajax_referer( 'frm_email_style_preview' );
267
268 $style_key = FrmAppHelper::get_param( 'style_key', '', 'get', 'sanitize_text_field' );
269 $not_exist_msg = __( "This email style doesn't exist", 'formidable' );
270
271 if ( ! $style_key ) {
272 die( esc_html( $not_exist_msg ) );
273 }
274
275 if ( ! isset( self::get_email_styles()[ $style_key ] ) ) {
276 die( esc_html( $not_exist_msg ) );
277 }
278
279 $style_key = FrmAppHelper::get_param( 'style_key', '', 'sanitize_text_field' );
280 $content = self::get_test_email_content( $style_key );
281
282 header( self::get_content_type_header( $style_key ) );
283
284 echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
285
286 die();
287 }
288
289 /**
290 * AJAX handler for sending a test email.
291 */
292 public static function ajax_send_test_email() {
293 // Check permission and nonce.
294 FrmAppHelper::permission_check( 'frm_change_settings' );
295 check_ajax_referer( 'frm_ajax', 'nonce' );
296
297 $emails_str = FrmAppHelper::get_post_param( 'emails_str', '', 'sanitize_text_field' );
298 $emails = explode( ',', $emails_str );
299 $valid_emails = array();
300
301 foreach ( $emails as $email ) {
302 $email = trim( $email );
303
304 if ( ! $email || ! is_email( $email ) ) {
305 continue;
306 }
307
308 $valid_emails[] = $email;
309 }
310
311 if ( ! $valid_emails ) {
312 wp_send_json_error( __( 'Invalid email address', 'formidable' ) );
313 }
314
315 $email_style = self::get_default_email_style();
316 $subject = __( 'Formidable Test Email', 'formidable' );
317 $content = self::get_test_email_content();
318 $headers = array(
319 self::get_content_type_header( $email_style ),
320 );
321
322 FrmUsageController::update_flows_data( 'send_test_email', $email_style );
323
324 $result = wp_mail( $valid_emails, $subject, $content, $headers );
325
326 if ( $result ) {
327 wp_send_json_success( __( 'Test email sent successfully!', 'formidable' ) );
328 }
329
330 wp_send_json_error( __( 'Failed to send test email!', 'formidable' ) );
331 }
332
333 /**
334 * Gets Content-Type header.
335 *
336 * @param string $email_style Email style.
337 *
338 * @return string
339 */
340 private static function get_content_type_header( $email_style ) {
341 $content_type = 'plain' === $email_style ? 'text/plain' : 'text/html';
342 return 'Content-Type: ' . $content_type . '; charset=utf-8';
343 }
344
345 /**
346 * Shows placeholder Pro settings.
347 *
348 * @return void
349 */
350 public static function show_upsell_settings() {
351 include FrmAppHelper::plugin_path() . '/classes/views/frm-settings/email/settings.php';
352 }
353
354 /**
355 * Gets email style settings value.
356 *
357 * @return array
358 */
359 public static function get_email_style_settings() {
360 /**
361 * Filter the email style settings value.
362 *
363 * @since 6.25
364 *
365 * @param array $settings The settings value.
366 */
367 return apply_filters(
368 'frm_email_style_settings',
369 array(
370 // Placeholder image.
371 'img' => FrmAppHelper::plugin_url() . '/images/email-styles/placeholder.png',
372 'img_size' => '',
373 'img_align' => '',
374 'img_location' => '',
375 'bg_color' => '#EAECF0',
376 'container_bg_color' => '#ffffff',
377 'text_color' => '#475467',
378 'link_color' => '#4199FD',
379 'border_color' => '#dddddd',
380 'font' => '',
381 )
382 );
383 }
384
385 /**
386 * Wraps email message with new settings.
387 *
388 * @param string $message Email message.
389 *
390 * @return string
391 */
392 public static function wrap_email_message( $message ) {
393 $style_settings = self::get_email_style_settings();
394 $header_img = '';
395
396 if ( $style_settings['img'] ) {
397 $img_align = $style_settings['img_align'] ? $style_settings['img_align'] : 'center';
398 $img_size = $style_settings['img_size'] ? $style_settings['img_size'] : 'thumbnail';
399 $img_url = is_numeric( $style_settings['img'] ) ? wp_get_attachment_image_url( $style_settings['img'], $img_size ) : $style_settings['img'];
400
401 $header_img .= sprintf(
402 '<div style="text-align:%s;margin-bottom:32px;">',
403 esc_attr( $img_align )
404 );
405
406 $header_img .= sprintf(
407 '<img src="%s" alt="" style="max-width:100%%;height:auto;" />',
408 esc_url( $img_url )
409 );
410
411 $header_img .= '</div>';
412 }
413
414 // Wrapper.
415 $font_family = $style_settings['font'] ? $style_settings['font'] : 'Inter,sans-serif';
416 $new_message = sprintf(
417 '<div style="background-color:%1$s;color:%2$s;font-family:%3$s;padding:40px 10px;">',
418 esc_attr( $style_settings['bg_color'] ),
419 esc_attr( $style_settings['text_color'] ),
420 esc_attr( $font_family )
421 );
422
423 // Container.
424 $new_message .= '<div style="max-width:640px;margin:auto;">';
425
426 // Header image if outside.
427 if ( $style_settings['img'] && 'inside' !== $style_settings['img_location'] ) {
428 $new_message .= $header_img;
429 }
430
431 // Main container.
432 $new_message .= sprintf(
433 '<div style="background-color:%s;border-radius:8px;padding:32px;">',
434 esc_attr( $style_settings['container_bg_color'] )
435 );
436
437 // Header image if inside.
438 if ( $style_settings['img'] && 'inside' === $style_settings['img_location'] ) {
439 $new_message .= $header_img;
440 }
441
442 // The message.
443 $new_message .= self::add_inline_css( 'a', 'color:' . $style_settings['link_color'] . ';', $message );
444
445 return $new_message . '</div></div></div>';
446 }
447
448 /**
449 * Adds inline CSS to a tag in the content.
450 *
451 * @param string $tag Tag name.
452 * @param string $css CSS code.
453 * @param string $content The content.
454 *
455 * @return string
456 */
457 private static function add_inline_css( $tag, $css, $content ) {
458 $regex = '/<' . $tag . '.*?>/msi';
459 preg_match_all( $regex, $content, $matches, PREG_SET_ORDER );
460
461 if ( ! $matches ) {
462 return $content;
463 }
464
465 $searches = array();
466 $replaces = array();
467
468 foreach ( $matches as $match ) {
469 $searches[] = $match[0];
470 $replaces[] = self::add_css_to_style_attr( $tag, $css, $match[0] );
471 }
472
473 return str_replace( $searches, $replaces, $content );
474 }
475
476 /**
477 * Adds inline CSS to a single HTML tag.
478 *
479 * @param string $tag Tag name.
480 * @param string $css CSS code.
481 * @param string $html The HTML tag.
482 *
483 * @return string
484 */
485 private static function add_css_to_style_attr( $tag, $css, $html ) {
486 $regex = '/\sstyle=("|\')/mi';
487
488 if ( preg_match( $regex, $html, $matches ) ) {
489 $search = $matches[0];
490 $replace = $search . $css;
491 return preg_replace( $regex, $replace, $html );
492 }
493
494 return str_replace( '<' . $tag, '<' . $tag . ' style="' . $css . '"', $html );
495 }
496 }
497