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

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