PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.1.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.1.1
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 / models / FrmStyle.php

FrmStyle.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.1.1, at classes/models/FrmStyle.php

684 lines 20.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmStyle {
7 public $number = false; // Unique ID number of the current instance.
8 public $id = 0; // the id of the post
9
10 /**
11 * @param int|string $id The id of the stylsheet or 'default'
12 */
13 public function __construct( $id = 0 ) {
14 $this->id = $id;
15 }
16
17 /**
18 * @return stdClass
19 */
20 public function get_new() {
21 $this->id = 0;
22
23 $max_slug_value = 2147483647;
24 $min_slug_value = 37; // we want to have at least 2 characters in the slug
25 $key = base_convert( rand( $min_slug_value, $max_slug_value ), 10, 36 );
26
27 $style = array(
28 'post_type' => FrmStylesController::$post_type,
29 'ID' => '',
30 'post_title' => __( 'New Style', 'formidable' ),
31 'post_name' => $key,
32 'post_content' => $this->get_defaults(),
33 'menu_order' => '',
34 'post_status' => 'publish',
35 );
36
37 return (object) $style;
38 }
39
40 /**
41 * @param array $settings
42 * @return int|WP_Error
43 */
44 public function save( $settings ) {
45 return FrmDb::save_settings( $settings, 'frm_styles' );
46 }
47
48 public function duplicate( $id ) {
49 // Duplicating is a pro feature. This is handled in FrmProStyle::duplicate instead.
50 }
51
52 /**
53 * Handle save actions in the visual styler edit page.
54 *
55 * @param mixed $id
56 * @return array<int|WP_Error>
57 */
58 public function update( $id = 'default' ) {
59 $all_instances = $this->get_all();
60
61 if ( ! $id ) {
62 $new_style = (array) $this->get_new();
63 $all_instances[] = $new_style;
64 }
65
66 $action_ids = array();
67
68 foreach ( $all_instances as $number => $new_instance ) {
69 $new_instance = (array) $new_instance;
70 $this->id = $new_instance['ID'];
71
72 if ( $id != $this->id || ! $_POST || ! isset( $_POST['frm_style_setting'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
73 // Don't continue if not saving this style.
74 continue;
75 }
76
77 // Custom CSS is no longer used from the default style, but it is still checked if the Global Setting is missing.
78 // Preserve the previous value in case Custom CSS has not been saved as a Global Setting yet.
79 $custom_css = isset( $new_instance['post_content']['custom_css'] ) ? $new_instance['post_content']['custom_css'] : '';
80
81 // phpcs:ignore WordPress.Security.NonceVerification.Missing
82 if ( ! empty( $_POST['frm_style_setting']['post_title'] ) ) {
83 // The nonce check happens in FrmStylesController::save_style before this is called.
84 // phpcs:ignore WordPress.Security.NonceVerification.Missing
85 $new_instance['post_title'] = sanitize_text_field( wp_unslash( $_POST['frm_style_setting']['post_title'] ) );
86 }
87
88 $new_instance['post_content'] = isset( $_POST['frm_style_setting']['post_content'] ) ? $this->sanitize_post_content( wp_unslash( $_POST['frm_style_setting']['post_content'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
89 $new_instance['post_content']['custom_css'] = $custom_css;
90 unset( $custom_css );
91
92 $new_instance['post_type'] = FrmStylesController::$post_type;
93 $new_instance['post_status'] = 'publish';
94
95 if ( ! $id ) {
96 $new_instance['post_name'] = $new_instance['post_title'];
97 }
98
99 $default_settings = $this->get_defaults();
100
101 foreach ( $default_settings as $setting => $default ) {
102 if ( ! isset( $new_instance['post_content'][ $setting ] ) ) {
103 $new_instance['post_content'][ $setting ] = $default;
104 }
105
106 if ( $this->is_color( $setting ) ) {
107 $color_val = $new_instance['post_content'][ $setting ];
108 if ( $color_val !== '' && false !== strpos( $color_val, 'rgb' ) ) {
109 // Maybe sanitize if invalid rgba value is entered.
110 $this->maybe_sanitize_rgba_value( $color_val );
111 }
112 $new_instance['post_content'][ $setting ] = str_replace( '#', '', $color_val );
113 } elseif ( in_array( $setting, array( 'submit_style', 'important_style', 'auto_width' ), true ) && ! isset( $new_instance['post_content'][ $setting ] ) ) {
114 $new_instance['post_content'][ $setting ] = 0;
115 } elseif ( $setting === 'font' ) {
116 $new_instance['post_content'][ $setting ] = $this->force_balanced_quotation( $new_instance['post_content'][ $setting ] );
117 }
118 }
119
120 $action_ids[] = $this->save( $new_instance );
121 }
122
123 $this->save_settings();
124
125 return $action_ids;
126 }
127
128 /**
129 * Sanitize custom color values and convert it to valid one filling missing values.
130 *
131 * @since 5.3.2
132 *
133 * @param string $color_val, The color value, by reference.
134 * @return void
135 */
136 private function maybe_sanitize_rgba_value( &$color_val ) {
137 if ( preg_match( '/(rgb|rgba)\(/', $color_val ) !== 1 ) {
138 return;
139 }
140
141 $color_val = trim( $color_val );
142 $color_val = ltrim( $color_val, '(' ); // Remove leading braces so (rgba(1,1,1,1) doesn't cause inconsistent braces.
143 $patterns = array( '/rgba\((\s*\d+\s*,){3}[[0-1]\.]+\)/', '/rgb\((\s*\d+\s*,){2}\s*[\d]+\)/' );
144 foreach ( $patterns as $pattern ) {
145 if ( preg_match( $pattern, $color_val ) === 1 ) {
146 return;
147 }
148 }
149
150 // Remove all leading ')' braces, then add one back. This way there's always a single brace.
151 $color_val = rtrim( $color_val, ')' );
152 $color_val .= ')';
153
154 $color_rgba = substr( $color_val, strpos( $color_val, '(' ) + 1, strlen( $color_val ) - strpos( $color_val, '(' ) - 2 );
155 $color_rgba = trim( $color_rgba, '()' ); // Remove any excessive braces from the rgba like rgba((.
156 $length_of_color_codes = strpos( $color_val, '(' );
157 $new_color_values = array();
158
159 // replace empty values by 0 or 1 (if alpha position).
160 foreach ( explode( ',', $color_rgba ) as $index => $value ) {
161 $new_value = null;
162 $value_is_empty_string = '' === trim( $value ) || '' === $value;
163
164 if ( 3 === $length_of_color_codes || ( $index !== $length_of_color_codes - 1 ) ) {
165 // Insert a value for r, g, or b.
166 if ( $value < 0 ) {
167 $new_value = 0;
168 } elseif ( $value > 255 ) {
169 $new_value = 255;
170 } elseif ( $value_is_empty_string ) {
171 $new_value = 0;
172 } else {
173 $new_value = absint( $value );
174 }
175 } else {
176 // Insert a value for alpha.
177 if ( $value_is_empty_string ) {
178 $new_value = 4 === $length_of_color_codes ? 1 : 0;
179 } elseif ( $value > 1 || $value < 0 ) {
180 $new_value = 1;
181 } else {
182 $new_value = floatval( $value );
183 }
184 }
185
186 $new_color_values[] = null === $new_value ? $value : $new_value;
187 }
188
189 // add more 0s and 1 (if alpha position) if needed.
190 $missing_values = $length_of_color_codes - count( $new_color_values );
191 if ( $missing_values > 1 ) {
192 $insert_values = array_fill( 0, $missing_values - 1, 0 );
193 $last_value = 4 === $length_of_color_codes ? 1 : 0;
194 array_push( $insert_values, $last_value );
195 } elseif ( $missing_values === 1 ) {
196 $insert_values = 4 === $length_of_color_codes ? array( 1 ) : array( 0 );
197 }
198 if ( ! empty( $insert_values ) ) {
199 $new_color_values = array_merge( $new_color_values, $insert_values );
200 }
201
202 $new_color = implode( ',', $new_color_values );
203 $prefix = substr( $color_val, 0, strpos( $color_val, '(' ) + 1 );
204 $prefix = rtrim( $prefix, '(' ) . '('; // Limit the number of opening braces after rgb/rgba. There should only be one.
205 $new_color = $prefix . $new_color . ')';
206
207 $color_val = $new_color;
208 }
209
210 /**
211 * Unslash everything in post_content but custom_css
212 *
213 * @since 5.0.13
214 *
215 * @param array $settings
216 * @return array
217 */
218 private function unslash_post_content( $settings ) {
219 $custom_css = isset( $settings['custom_css'] ) ? $settings['custom_css'] : '';
220 $settings = wp_unslash( $settings );
221 $settings['custom_css'] = $custom_css;
222 return $settings;
223 }
224
225 /**
226 * @since 5.0.13
227 *
228 * @param array $settings
229 * @return array
230 */
231 public function sanitize_post_content( $settings ) {
232 $defaults = $this->get_defaults();
233 $valid_keys = array_keys( $defaults );
234 $sanitized_settings = array();
235 foreach ( $valid_keys as $key ) {
236 if ( isset( $settings[ $key ] ) ) {
237 $sanitized_settings[ $key ] = sanitize_textarea_field( $settings[ $key ] );
238 } else {
239 $sanitized_settings[ $key ] = $defaults[ $key ];
240 }
241 }
242 return $sanitized_settings;
243 }
244
245 /**
246 * @since 3.01.01
247 */
248 private function is_color( $setting ) {
249 $extra_colors = array( 'error_bg', 'error_border', 'error_text' );
250
251 return strpos( $setting, 'color' ) !== false || in_array( $setting, $extra_colors );
252 }
253
254 /**
255 * @since 3.01.01
256 *
257 * @return array
258 */
259 public function get_color_settings() {
260 $defaults = $this->get_defaults();
261 $settings = array_keys( $defaults );
262
263 return array_filter( $settings, array( $this, 'is_color' ) );
264 }
265
266 /**
267 * Create static CSS file and update the CSS transient alternative.
268 *
269 * @return void
270 */
271 public function save_settings() {
272 $filename = FrmAppHelper::plugin_path() . '/css/custom_theme.css.php';
273 update_option( 'frm_last_style_update', gmdate( 'njGi' ) );
274
275 if ( ! is_file( $filename ) ) {
276 return;
277 }
278
279 $this->clear_cache();
280
281 $css = $this->get_css_content( $filename );
282 $create_file = new FrmCreateFile(
283 array(
284 'file_name' => FrmStylesController::get_file_name(),
285 'new_file_path' => FrmAppHelper::plugin_path() . '/css',
286 )
287 );
288 $create_file->create_file( $css );
289
290 update_option( 'frmpro_css', $css, 'no' );
291 set_transient( 'frmpro_css', $css, MONTH_IN_SECONDS );
292 }
293
294 /**
295 * @param string $filename
296 * @return string
297 */
298 private function get_css_content( $filename ) {
299 $css = '/* ' . __( 'WARNING: Any changes made to this file will be lost when your Formidable settings are updated', 'formidable' ) . ' */' . "\n";
300
301 $saving = true;
302 $frm_style = $this;
303
304 ob_start();
305 include $filename;
306 $css .= preg_replace( '/\/\*(.|\s)*?\*\//', '', str_replace( array( "\r\n", "\r", "\n", "\t", ' ' ), '', ob_get_contents() ) );
307 ob_end_clean();
308
309 return FrmStylesController::replace_relative_url( $css );
310 }
311
312 /**
313 * @return void
314 */
315 private function clear_cache() {
316 $default_post_atts = array(
317 'post_type' => FrmStylesController::$post_type,
318 'post_status' => 'publish',
319 'numberposts' => 99,
320 'orderby' => 'title',
321 'order' => 'ASC',
322 );
323
324 FrmDb::delete_cache_and_transient( json_encode( $default_post_atts ), 'frm_styles' );
325 FrmDb::cache_delete_group( 'frm_styles' );
326 FrmDb::delete_cache_and_transient( 'frmpro_css' );
327 }
328
329 /**
330 * Delete a style by its post ID.
331 *
332 * @param int $id
333 * @return WP_Post|false|null
334 */
335 public function destroy( $id ) {
336 if ( $id === $this->get_default_style()->ID ) {
337 return false;
338 }
339 return wp_delete_post( $id );
340 }
341
342 /**
343 * @return WP_Post|stdClass
344 */
345 public function get_one() {
346 if ( 'default' === $this->id ) {
347 $style = $this->get_default_style();
348 if ( $style ) {
349 $this->id = $style->ID;
350 } else {
351 $this->id = 0;
352 }
353
354 return $style;
355 }
356
357 $style = get_post( $this->id );
358
359 if ( ! $style ) {
360 return $style;
361 }
362
363 $style->post_content = FrmAppHelper::maybe_json_decode( $style->post_content );
364
365 $default_values = $this->get_defaults();
366
367 // fill default values
368 $style->post_content = $this->override_defaults( $style->post_content );
369 $style->post_content = wp_parse_args( $style->post_content, $default_values );
370
371 return $style;
372 }
373
374 /**
375 * @param string $orderby
376 * @param string $order
377 * @param int $limit
378 * @return array
379 */
380 public function get_all( $orderby = 'title', $order = 'ASC', $limit = 99 ) {
381 $post_atts = array(
382 'post_type' => FrmStylesController::$post_type,
383 'post_status' => 'publish',
384 'numberposts' => $limit,
385 'orderby' => $orderby,
386 'order' => $order,
387 );
388
389 $temp_styles = FrmDb::check_cache( json_encode( $post_atts ), 'frm_styles', $post_atts, 'get_posts' );
390
391 if ( empty( $temp_styles ) ) {
392 global $wpdb;
393 // make sure there wasn't a conflict with the query
394 $query = $wpdb->prepare( 'SELECT * FROM ' . $wpdb->posts . ' WHERE post_type=%s AND post_status=%s ORDER BY post_title ASC LIMIT 99', FrmStylesController::$post_type, 'publish' );
395 $temp_styles = FrmDb::check_cache( 'frm_backup_style_check', 'frm_styles', $query, 'get_results' );
396
397 if ( empty( $temp_styles ) ) {
398 // create a new style if there are none
399 $new = $this->get_new();
400 $new->post_title = __( 'Formidable Style', 'formidable' );
401 $new->post_name = $new->post_title;
402 $new->menu_order = 1;
403 $new = $this->save( (array) $new );
404 $this->update( 'default' );
405
406 $post_atts['include'] = $new;
407
408 $temp_styles = get_posts( $post_atts );
409 }
410 }
411
412 $default_values = $this->get_defaults();
413 $default_style = false;
414
415 $styles = array();
416 foreach ( $temp_styles as $style ) {
417 $this->id = $style->ID;
418 if ( $style->menu_order ) {
419 if ( $default_style ) {
420 // only return one default
421 $style->menu_order = 0;
422 } else {
423 // check for a default style
424 $default_style = $style->ID;
425 }
426 }
427
428 $style->post_content = FrmAppHelper::maybe_json_decode( $style->post_content );
429
430 // fill default values
431 $style->post_content = $this->override_defaults( $style->post_content );
432 $style->post_content = wp_parse_args( $style->post_content, $default_values );
433
434 $styles[ $style->ID ] = $style;
435 }
436
437 if ( ! $default_style ) {
438 $default_style = reset( $styles );
439
440 $styles[ $default_style->ID ]->menu_order = 1;
441 }
442
443 return $styles;
444 }
445
446 public function get_default_style( $styles = null ) {
447 if ( ! isset( $styles ) ) {
448 $styles = $this->get_all( 'menu_order', 'DESC', 1 );
449 }
450
451 foreach ( $styles as $style ) {
452 if ( $style->menu_order ) {
453 return $style;
454 }
455 }
456 }
457
458 /**
459 * @param mixed $settings
460 * @return mixed
461 */
462 public function override_defaults( $settings ) {
463 if ( ! is_array( $settings ) ) {
464 return $settings;
465 }
466
467 $settings['line_height'] = ( ! isset( $settings['field_height'] ) || $settings['field_height'] == '' || $settings['field_height'] == 'auto' ) ? 'normal' : $settings['field_height'];
468
469 if ( ! isset( $settings['form_desc_size'] ) && isset( $settings['description_font_size'] ) ) {
470 $settings['form_desc_size'] = $settings['description_font_size'];
471 $settings['form_desc_color'] = $settings['description_color'];
472 $settings['title_color'] = $settings['label_color'];
473 }
474
475 if ( ! isset( $settings['section_color'] ) && isset( $settings['label_color'] ) ) {
476 $settings['section_color'] = $settings['label_color'];
477 $settings['section_border_color'] = $settings['border_color'];
478 }
479
480 if ( ! isset( $settings['submit_hover_bg_color'] ) && isset( $settings['submit_bg_color'] ) ) {
481 $settings['submit_hover_bg_color'] = $settings['submit_bg_color'];
482 $settings['submit_hover_color'] = $settings['submit_text_color'];
483 $settings['submit_hover_border_color'] = $settings['submit_border_color'];
484
485 $settings['submit_active_bg_color'] = $settings['submit_bg_color'];
486 $settings['submit_active_color'] = $settings['submit_text_color'];
487 $settings['submit_active_border_color'] = $settings['submit_border_color'];
488 }
489
490 return apply_filters( 'frm_override_default_styles', $settings );
491 }
492
493 /**
494 * @return array
495 */
496 public function get_defaults() {
497 $defaults = array(
498 'theme_css' => 'ui-lightness',
499 'theme_name' => 'UI Lightness',
500
501 'center_form' => '',
502 'form_width' => '100%',
503 'form_align' => 'left',
504 'direction' => is_rtl() ? 'rtl' : 'ltr',
505 'fieldset' => '0px',
506 'fieldset_color' => '000000',
507 'fieldset_padding' => '0 0 15px 0',
508 'fieldset_bg_color' => '',
509
510 'title_size' => '40px',
511 'title_color' => '444444',
512 'title_margin_top' => '10px',
513 'title_margin_bottom' => '60px',
514 'form_desc_size' => '14px',
515 'form_desc_color' => '666666',
516 'form_desc_margin_top' => '10px',
517 'form_desc_margin_bottom' => '25px',
518 'form_desc_padding' => '0',
519
520 'font' => '',
521 'font_size' => '15px',
522 'label_color' => '3f4b5b',
523 'weight' => 'normal',
524 'position' => 'none',
525 'align' => 'left',
526 'width' => '150px',
527 'required_color' => 'B94A48',
528 'required_weight' => 'bold',
529 'label_padding' => '0 0 3px 0',
530
531 'description_font_size' => '12px',
532 'description_color' => '666666',
533 'description_weight' => 'normal',
534 'description_style' => 'normal',
535 'description_align' => 'left',
536 'description_margin' => '0',
537
538 'field_font_size' => '14px',
539 'field_height' => '32px',
540 'line_height' => 'normal',
541 'field_width' => '100%',
542 'auto_width' => false,
543 'field_pad' => '6px 10px',
544 'field_margin' => '20px',
545 'field_weight' => 'normal',
546 'text_color' => '555555',
547 //'border_color_hv' => 'cccccc',
548 'border_color' => 'BFC3C8',
549 'field_border_width' => '1px',
550 'field_border_style' => 'solid',
551
552 'bg_color' => 'ffffff',
553 //'bg_color_hv' => 'ffffff',
554 'remove_box_shadow' => '',
555 'bg_color_active' => 'ffffff',
556 'border_color_active' => '66afe9',
557 'remove_box_shadow_active' => '',
558 'text_color_error' => '444444',
559 'bg_color_error' => 'ffffff',
560 'border_color_error' => 'B94A48',
561 'border_width_error' => '1px',
562 'border_style_error' => 'solid',
563 'bg_color_disabled' => 'ffffff',
564 'border_color_disabled' => 'E5E5E5',
565 'text_color_disabled' => 'A1A1A1',
566
567 'radio_align' => 'block',
568 'check_align' => 'block',
569 'check_font_size' => '13px',
570 'check_label_color' => '444444',
571 'check_weight' => 'normal',
572
573 'section_font_size' => '18px',
574 'section_color' => '444444',
575 'section_weight' => 'bold',
576 'section_pad' => '15px 0 3px 0',
577 'section_mar_top' => '15px',
578 'section_mar_bottom' => '30px',
579 'section_bg_color' => '',
580 'section_border_color' => 'e8e8e8',
581 'section_border_width' => '2px',
582 'section_border_style' => 'solid',
583 'section_border_loc' => '-top',
584 'collapse_icon' => '6',
585 'collapse_pos' => 'after',
586 'repeat_icon' => '1',
587 'repeat_icon_color' => 'ffffff',
588
589 'submit_style' => false,
590 'submit_font_size' => '15px',
591 'submit_width' => 'auto',
592 'submit_height' => 'auto',
593 'submit_bg_color' => '579AF6',
594 'submit_border_color' => '579AF6',
595 'submit_border_width' => '1px',
596 'submit_text_color' => 'ffffff',
597 'submit_weight' => 'normal',
598 'submit_border_radius' => '4px',
599 'submit_bg_img' => '',
600 'submit_margin' => '10px',
601 'submit_padding' => '10px 20px',
602 'submit_shadow_color' => 'eeeeee',
603 'submit_hover_bg_color' => 'efefef',
604 'submit_hover_color' => '444444',
605 'submit_hover_border_color' => 'cccccc',
606 'submit_active_bg_color' => 'efefef',
607 'submit_active_color' => '444444',
608 'submit_active_border_color' => 'cccccc',
609
610 'border_radius' => '4px',
611 'error_bg' => 'F2DEDE',
612 'error_border' => 'EBCCD1',
613 'error_text' => 'B94A48',
614 'error_font_size' => '14px',
615
616 'success_bg_color' => 'DFF0D8',
617 'success_border_color' => 'D6E9C6',
618 'success_text_color' => '468847',
619 'success_font_size' => '14px',
620
621 'important_style' => false,
622
623 'progress_bg_color' => 'eaeaea',
624 'progress_active_color' => 'ffffff',
625 'progress_active_bg_color' => '579AF6',
626 'progress_color' => '3f4b5b',
627 'progress_border_color' => 'E5E5E5',
628 'progress_border_size' => '2px',
629 'progress_size' => '24px',
630
631 'custom_css' => '',
632 );
633
634 return apply_filters( 'frm_default_style_settings', $defaults );
635 }
636
637 /**
638 * Get a name attribute value for a style setting input.
639 *
640 * @param string $field_name
641 * @param string $post_field
642 * @return string
643 */
644 public function get_field_name( $field_name, $post_field = 'post_content' ) {
645 return 'frm_style_setting' . ( empty( $post_field ) ? '' : '[' . $post_field . ']' ) . '[' . $field_name . ']';
646 }
647
648 /**
649 * @return array
650 */
651 public static function get_bold_options() {
652 return array(
653 100 => 100,
654 200 => 200,
655 300 => 300,
656 'normal' => __( 'normal', 'formidable' ),
657 500 => 500,
658 600 => 600,
659 'bold' => __( 'bold', 'formidable' ),
660 800 => 800,
661 900 => 900,
662 );
663 }
664
665 /**
666 * Don't let imbalanced font families ruin the whole stylesheet
667 *
668 * @param string $value
669 * @return string
670 */
671 public function force_balanced_quotation( $value ) {
672 $balanced_characters = array( '"', "'" );
673 foreach ( $balanced_characters as $char ) {
674 $char_count = substr_count( $value, $char );
675 $is_balanced = $char_count % 2 == 0;
676 if ( ! $is_balanced ) {
677 $value .= $char;
678 }
679 }
680
681 return $value;
682 }
683 }
684