__( 'top', 'formidable' ),
'left' => __( 'left', 'formidable' ),
'right' => __( 'right', 'formidable' ),
'no_label' => __( 'none', 'formidable' ),
'inside' => __( 'inside', 'formidable' ),
);
}
/**
* @since 6.11 Added $field param.
*
* @param array|object $field
*
* @return array
*/
public static function get_single_label_positions( $field = array() ) {
$label_positions = array(
'top' => __( 'Top', 'formidable' ),
'left' => __( 'Left', 'formidable' ),
'right' => __( 'Right', 'formidable' ),
'inline' => __( 'Inline (left without a set width)', 'formidable' ),
'none' => __( 'None', 'formidable' ),
'hidden' => __( 'Hidden (but leave the space)', 'formidable' ),
'inside' => __( 'Placeholder inside the field', 'formidable' ),
);
/**
* Allows updating label positions in field settings.
*
* @since 6.11
*
* @param array $label_positions
* @param array|object $field
*/
return apply_filters( 'frm_single_label_positions', $label_positions, $field );
}
public static function minus_icons() {
return array(
0 => array(
'-' => '62e',
'+' => '62f',
),
1 => array(
'-' => '600',
'+' => '602',
),
2 => array(
'-' => '604',
'+' => '603',
),
3 => array(
'-' => '633',
'+' => '632',
),
4 => array(
'-' => '613',
'+' => '60f',
),
);
}
public static function arrow_icons() {
$minus_icons = self::minus_icons();
return array(
6 => array(
'-' => '62d',
'+' => '62a',
),
0 => array(
'-' => '60d',
'+' => '609',
),
1 => array(
'-' => '60e',
'+' => '60c',
),
2 => array(
'-' => '630',
'+' => '631',
),
3 => array(
'-' => '62b',
'+' => '628',
),
4 => array(
'-' => '62c',
'+' => '629',
),
5 => array(
'-' => '635',
'+' => '634',
),
'p0' => $minus_icons[0],
'p1' => $minus_icons[1],
'p2' => $minus_icons[2],
'p3' => $minus_icons[3],
'p4' => $minus_icons[4],
);
}
/**
* @since 2.0
* @return string The class for this icon.
*/
public static function icon_key_to_class( $key, $icon = '+', $type = 'arrow' ) {
if ( 'arrow' === $type && is_numeric( $key ) ) {
// frm_arrowup6_icon.
$arrow = array(
'-' => 'down',
'+' => 'up',
);
$class = 'frm_arrow' . $arrow[ $icon ];
} else {
// frm_minus1_icon.
$key = str_replace( 'p', '', $key );
$plus = array(
'-' => 'minus',
'+' => 'plus',
);
$class = 'frm_' . $plus[ $icon ];
}
if ( $key ) {
$class .= $key;
}
$class .= '_icon';
return $class;
}
/**
* @param WP_Post $style
* @param FrmStyle $frm_style
* @param string $type
* @return void
*/
public static function bs_icon_select( $style, $frm_style, $type = 'arrow' ) {
$function_name = $type . '_icons';
$icons = self::$function_name();
unset( $function_name );
$name = 'arrow' === $type ? 'collapse_icon' : 'repeat_icon';
?>
including three numeric values for R, G, and B.
*/
private static function get_rgb_array_from_rgb( $rgb ) {
$rgb = str_replace( array( 'rgb(', 'rgba(', ')' ), '', $rgb );
$rgb = explode( ',', $rgb );
if ( 4 === count( $rgb ) ) {
// Drop the alpha. The function is expected to only return r,g,b with no alpha.
array_pop( $rgb );
}
return $rgb;
}
/**
* Get the R, G, and B array values from a Hex color code.
*
* @since 6.8.3
*
* @param string $hex A hex color string.
* @return array Including three numeric values for R, G, and B.
*/
private static function get_rgb_array_from_hex( $hex ) {
$hex = str_replace( '#', '', $hex );
list( $r, $g, $b ) = sscanf( $hex, '%02x%02x%02x' );
return array( $r, $g, $b );
}
/**
* @since 4.0
*/
public static function hex2rgba( $hex, $a ) {
$rgb = self::hex2rgb( $hex );
return 'rgba(' . $rgb . ',' . $a . ')';
}
/**
* @since 6.0
*
* @param string $rgba Color setting value. This could be hex or rgb.
* @return string Hex color value.
*/
private static function rgb_to_hex( $rgba ) {
if ( strpos( $rgba, '#' ) === 0 ) {
// Color is already hex.
return $rgba;
}
preg_match( '/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i', $rgba, $by_color );
return sprintf( '%02x%02x%02x', $by_color[1], $by_color[2], $by_color[3] );
}
/**
* @since 6.8
*
* @param string $hsl
* @return string|null Null if it fails to parse the HSL string.
*/
private static function hsl_to_hex( $hsl ) {
// Convert hsla to hsl.
$hsl = preg_replace( '/hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/', 'hsl($1, $2%, $3%)', $hsl );
// Extract HSL components from the color string.
preg_match( '/hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)/', $hsl, $matches );
if ( count( $matches ) !== 4 ) {
// Invalid HSL string format.
return null;
}
// Extract HSL values.
$h = (int) $matches[1];
$s = (int) $matches[2] / 100;
$l = (int) $matches[3] / 100;
// Calculate RGB values.
$c = ( 1 - abs( 2 * $l - 1 ) ) * $s;
$x = $c * ( 1 - abs( ( (int) ( $h / 60 ) % 2 ) - 1 ) );
$m = $l - $c / 2;
$r = 0;
$g = 0;
$b = 0;
if ( $h >= 0 && $h < 60 ) {
$r = $c;
$g = $x;
} elseif ( $h >= 60 && $h < 120 ) {
$r = $x;
$g = $c;
} elseif ( $h >= 120 && $h < 180 ) {
$g = $c;
$b = $x;
} elseif ( $h >= 180 && $h < 240 ) {
$g = $x;
$b = $c;
} elseif ( $h >= 240 && $h < 300 ) {
$r = $x;
$b = $c;
} elseif ( $h >= 300 && $h < 360 ) {
$r = $c;
$b = $x;
}//end if
// Convert RGB to 8-bit values
$r = round( ( $r + $m ) * 255 );
$g = round( ( $g + $m ) * 255 );
$b = round( ( $b + $m ) * 255 );
// Convert RGB to hex
$hex = sprintf( '%02x%02x%02x', $r, $g, $b );
return $hex;
}
/**
* @since 2.3
* @param string $hex string The original color in hex format #ffffff.
* @param int $steps integer Should be between -255 and 255. Negative = darker, positive = lighter.
*/
public static function adjust_brightness( $hex, $steps ) {
$steps = max( - 255, min( 255, $steps ) );
if ( 0 === strpos( $hex, 'rgba(' ) ) {
$rgba = str_replace( ')', '', str_replace( 'rgba(', '', $hex ) );
list ( $r, $g, $b, $a ) = array_map( 'trim', explode( ',', $rgba ) );
$r = max( 0, min( 255, $r + $steps ) );
$g = max( 0, min( 255, $g + $steps ) );
$b = max( 0, min( 255, $b + $steps ) );
return 'rgba(' . $r . ',' . $g . ',' . $b . ',' . $a . ')';
}
// Normalize into a six character long hex string
$hex = str_replace( '#', '', $hex );
self::fill_hex( $hex );
// Split into three parts: R, G and B
$color_parts = str_split( $hex, 2 );
$return = '#';
foreach ( $color_parts as $color ) {
// Convert to decimal.
$color = hexdec( $color );
// Adjust color.
$color = max( 0, min( 255, $color + $steps ) );
// Make two char hex code.
$return .= str_pad( dechex( $color ), 2, '0', STR_PAD_LEFT );
}
return $return;
}
/**
* @since 6.0
*
* @param string $color
* @return int
*/
public static function get_color_brightness( $color ) {
if ( 0 === strpos( $color, 'rgb' ) ) {
$color = self::rgb_to_hex( $color );
}
if ( 0 === strpos( $color, 'hsl' ) ) {
$hsl_to_hex = self::hsl_to_hex( $color );
if ( is_null( $hsl_to_hex ) ) {
// Fallback if we cannot convert the HSL value.
return 0;
}
$color = $hsl_to_hex;
}
self::fill_hex( $color );
$c_r = hexdec( substr( $color, 0, 2 ) );
$c_g = hexdec( substr( $color, 2, 2 ) );
$c_b = hexdec( substr( $color, 4, 2 ) );
$brightness = ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000;
return $brightness;
}
/**
* Change a 3 character hex color to a 6 character one.
*
* @since 6.0
*
* @return void
*/
private static function fill_hex( &$color ) {
if ( 3 === strlen( $color ) ) {
$color = $color[0] . $color[0] . $color[1] . $color[1] . $color[2] . $color[2];
}
}
/**
* @since 4.05.02
*/
public static function get_css_vars( $vars = array() ) {
$vars = apply_filters( 'frm_css_vars', $vars );
return array_unique( $vars );
}
/**
* @since 4.05.02
*/
public static function output_vars( $settings, $defaults = array(), $vars = array() ) {
if ( empty( $vars ) ) {
$vars = self::get_css_vars( array_keys( $settings ) );
}
$remove = array( 'remove_box_shadow', 'remove_box_shadow_active', 'theme_css', 'theme_name', 'theme_selector', 'important_style', 'submit_style', 'collapse_icon', 'center_form', 'custom_css', 'style_class', 'submit_bg_img', 'change_margin', 'repeat_icon' );
$vars = array_diff( $vars, $remove );
foreach ( $vars as $var ) {
if ( ! isset( $settings[ $var ] ) ) {
continue;
}
if ( ! isset( $defaults[ $var ] ) ) {
$defaults[ $var ] = '';
}
$show = empty( $defaults ) || ( $settings[ $var ] !== '' && $settings[ $var ] !== $defaults[ $var ] );
if ( $show ) {
echo '--' . esc_html( str_replace( '_', '-', $var ) ) . ':' . self::css_var_prepare_value( $settings, $var ) . ';'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
}
/**
* Prepare the value for a CSS variable.
*
* @since 6.14
*
* @param array $settings An array of css style.
* @param string $key
*
* @return string
*/
private static function css_var_prepare_value( $settings, $key ) {
$value = $settings[ $key ];
switch ( $key ) {
case 'font':
return safecss_filter_attr( $value );
case 'border_width_error':
case 'field_border_width':
if ( ! empty( $settings['field_shape_type'] ) && 'underline' === $settings['field_shape_type'] ) {
return safecss_filter_attr( '0px 0px ' . $value . ' 0px' );
}
break;
case 'box_shadow':
if ( ! empty( $settings['field_shape_type'] ) && 'underline' === $settings['field_shape_type'] ) {
return safecss_filter_attr( 'none' );
}
break;
case 'border_radius':
if ( ! empty( $settings['field_shape_type'] ) ) {
switch ( $settings['field_shape_type'] ) {
case 'underline':
case 'regular':
return safecss_filter_attr( '0px' );
case 'circle':
return safecss_filter_attr( '30px' );
}
}
break;
}//end switch
return esc_html( $settings[ $key ] );
}
/**
* @since 2.3
*
* @param WP_Post $style
* @return array
*/
public static function get_settings_for_output( $style ) {
if ( self::previewing_style() ) {
$frm_style = new FrmStyle();
if ( isset( $_POST['frm_style_setting'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
// Sanitizing is done later.
$posted = wp_unslash( $_POST['frm_style_setting'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
if ( ! is_array( $posted ) ) {
$posted = json_decode( $posted, true );
FrmAppHelper::format_form_data( $posted );
$settings = $frm_style->sanitize_post_content( $posted['frm_style_setting']['post_content'] );
$style_name = sanitize_title( $posted['style_name'] );
} else {
$settings = $frm_style->sanitize_post_content( $posted['post_content'] );
$style_name = FrmAppHelper::get_post_param( 'style_name', '', 'sanitize_title' );
}
} else {
$settings = $frm_style->sanitize_post_content( wp_unslash( $_GET ) );
$style_name = FrmAppHelper::get_param( 'style_name', '', 'get', 'sanitize_title' );
}
$settings = self::update_base_font_size( $settings, $frm_style->get_defaults() );
FrmAppHelper::sanitize_value( 'sanitize_text_field', $settings );
$settings['style_class'] = '';
if ( ! empty( $style_name ) ) {
$settings['style_class'] = $style_name . '.';
}
} else {
$settings = $style->post_content;
$settings['style_class'] = 'frm_style_' . $style->post_name . '.';
}//end if
$settings['style_class'] .= 'with_frm_style';
$settings['font'] = stripslashes( $settings['font'] );
$settings['change_margin'] = self::description_margin_for_screensize( $settings['width'] );
$checkbox_opts = array( 'important_style', 'auto_width', 'submit_style', 'collapse_icon', 'center_form' );
foreach ( $checkbox_opts as $opt ) {
if ( ! isset( $settings[ $opt ] ) ) {
$settings[ $opt ] = 0;
}
}
self::prepare_color_output( $settings );
$settings['field_height'] = $settings['field_height'] === '' ? 'auto' : $settings['field_height'];
$settings['field_width'] = $settings['field_width'] === '' ? 'auto' : $settings['field_width'];
$settings['auto_width'] = $settings['auto_width'] ? 'auto' : $settings['field_width'];
$settings['box_shadow'] = ! empty( $settings['remove_box_shadow'] ) ? 'none' : '0 1px 2px 0 rgba(18, 18, 23, 0.05)';
if ( ! isset( $settings['repeat_icon'] ) ) {
$settings['repeat_icon'] = 1;
}
return $settings;
}
/**
* Update the "Base Font Size" value from "Quick Settings across multiple settings values".
*
* @since 6.14
*
* @param array $settings An array of css style.
*
* @return array
*/
public static function update_base_font_size( $settings, $defaults ) {
if ( empty( $settings['base_font_size'] ) || empty( $settings['use_base_font_size'] ) || 'false' === $settings['use_base_font_size'] ) {
return $settings;
}
$base_font_size = (int) $settings['base_font_size'];
$font_size = $settings['font_size'];
$font_sizes_to_update = array(
'font_size',
'field_font_size',
'check_font_size',
'title_size',
'form_desc_size',
'description_font_size',
'section_font_size',
'submit_font_size',
'success_font_size',
'error_font_size',
'progress_size',
);
array_map(
function ( $key ) use ( $defaults, $font_size, $base_font_size, &$settings ) {
if ( isset( $settings[ $key ] ) ) {
$settings[ $key ] = round( self::get_base_font_size_scale( $key, $font_size, $defaults ) * $base_font_size ) . 'px';
}
},
$font_sizes_to_update
);
return $settings;
}
/**
* Get style font size scale value.
*
* @since 6.14
*
* @return float
*/
private static function get_base_font_size_scale( $key, $value, $defaults ) {
if ( empty( $defaults[ $key ] ) || ! is_numeric( (int) $defaults[ $key ] ) || ! is_numeric( (int) $value ) ) {
return 1;
}
return round( (int) $defaults[ $key ] / (int) $value, 2 );
}
/**
* @since 2.3
*/
public static function prepare_color_output( &$settings, $allow_transparent = true ) {
$colors = self::allow_color_override();
foreach ( $colors as $css => $opts ) {
if ( $css === 'transparent' && ! $allow_transparent ) {
$css = '';
}
foreach ( $opts as $opt ) {
self::get_color_output( $css, $settings[ $opt ] );
}
}
}
/**
* @since 2.3
*
* @return array
*/
private static function allow_color_override() {
$frm_style = new FrmStyle();
$colors = $frm_style->get_color_settings();
$transparent = array(
'fieldset_color',
'fieldset_bg_color',
'bg_color',
'bg_color_active',
'bg_color_disabled',
'section_bg_color',
'error_bg',
'success_bg_color',
'progress_bg_color',
'progress_active_bg_color',
'submit_border_color',
'submit_hover_border_color',
'submit_active_border_color',
'submit_hover_bg_color',
'submit_active_bg_color',
);
return array(
'transparent' => $transparent,
'' => array_diff( $colors, $transparent ),
);
}
/**
* @since 2.3
*/
private static function get_color_output( $default, &$color ) {
$color = trim( $color );
if ( empty( $color ) ) {
$color = $default;
} elseif ( false !== strpos( $color, 'rgb(' ) ) {
$color = str_replace( 'rgb(', 'rgba(', $color );
$color = str_replace( ')', ',1)', $color );
} elseif ( strpos( $color, '#' ) === false && self::is_hex( $color ) ) {
$color = '#' . $color;
}
}
/**
* If a color looks like a hex code without the #, prepend the #.
* A color looks like a hex code if it does not contain the substrings "rgb", "rgba", "hsl", "hsla", or "hwb".
*
* @since 6.8
*
* @param string $color
* @return bool
*/
private static function is_hex( $color ) {
$non_hex_substrings = array(
'rgba(',
'hsl(',
'hsla(',
'hwb(',
);
foreach ( $non_hex_substrings as $substring ) {
if ( false !== strpos( $color, $substring ) ) {
return false;
}
}
return true;
}
/**
* If left/right label is over a certain size,
* adjust the field description margin at a different screen size
*
* @since 2.3
*/
private static function description_margin_for_screensize( $width ) {
$temp_label_width = str_replace( 'px', '', $width );
$change_margin = false;
if ( $temp_label_width >= 230 ) {
$change_margin = '800px';
} elseif ( $width >= 215 ) {
$change_margin = '700px';
} elseif ( $width >= 180 ) {
$change_margin = '650px';
}
return $change_margin;
}
/**
* @since 2.3
*
* @return bool
*/
public static function previewing_style() {
$ajax_change = isset( $_POST['action'] ) && $_POST['action'] === 'frm_change_styling' && isset( $_POST['frm_style_setting'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
return $ajax_change || isset( $_GET['flat'] );
}
/**
* Get the URL for the Styler page list view (where you can assign styles to a form and view style templates) for a target form.
*
* @since 6.0
*
* @param int|string $form_id
* @return string
*/
public static function get_list_url( $form_id ) {
return admin_url( 'admin.php?page=formidable-styles&form=' . absint( $form_id ) );
}
/**
* Get the back button args from Style settings.
*
* @since 6.14
*
* @param stdClass|WP_Post $style
* @param int $form_id
* @return array
*/
public static function get_style_options_back_button_args( $style, $form_id ) {
if ( self::is_advanced_settings() ) {
return array(
'title' => __( 'Quick Settings', 'formidable' ),
'id' => 'frm_style_back_to_quick_settings',
);
}
return array(
'href' => self::get_list_url( $form_id ),
'title' => $style->post_title,
);
}
/**
* Get a link to edit a target style post object in the visual styler.
*
* @param stdClass|WP_Post $style
* @param int|string $form_id Used for the back button and preview form target.
* @param string $section The url param section.
*
* @return string
*/
public static function get_edit_url( $style, $form_id = 0, $section = '' ) {
$query_args = array(
'page' => 'formidable-styles',
'frm_action' => 'edit',
'id' => $style->ID,
'section' => $section,
);
if ( $form_id ) {
// We include &form_id for the back button to know where to point to.
$query_args['form'] = $form_id;
}
return add_query_arg( $query_args, admin_url( 'admin.php' ) );
}
/**
* Get a count of the number of forms assigned to a target style ID.
* All unassigned forms are included in the count for the default style.
*
* @since 6.0
*
* @param int|string $style_id
* @param bool $is_default
* @return int
*/
public static function get_form_count_for_style( $style_id, $is_default ) {
$serialized = serialize( array( 'custom_style' => (string) $style_id ) );
// Chop off the "a:1:{" from the front and the "}" from the back.
$substring = substr( $serialized, 5, -1 );
$number_of_forms = FrmDb::get_count(
'frm_forms',
array(
'status' => 'published',
'options LIKE' => $substring,
)
);
if ( ! $is_default ) {
// Exit early as the rest of the code is about including the default count.
return $number_of_forms;
}
$conversational_style_id = FrmDb::get_var( 'posts', array( 'post_name' => 'lines-no-boxes' ), 'ID' );
$number_of_forms += self::get_default_style_count( $style_id, $conversational_style_id );
return $number_of_forms;
}
/**
* Get the number of forms that use the default style.
*
* @since 6.0
*
* @param int|string $style_id
* @param mixed $conversational_style_id
* @return int
*/
private static function get_default_style_count( $style_id, $conversational_style_id ) {
$substrings = array_map(
function ( $value ) {
$substring = serialize( array( 'custom_style' => $value ) );
return substr( $substring, 5, -1 );
},
array( '1', 1 )
);
$where = array(
'status' => 'published',
0 => array(
'options NOT LIKE' => 'custom_style',
'or' => 1,
'options LIKE' => $substrings,
),
);
if ( $conversational_style_id ) {
// When a conversational style is set, check for it in the query by wrapping the where and adding a conversational option check.
$is_conversational_style = (int) $style_id === (int) $conversational_style_id;
$where[0] = array(
// The chat option doesn't exist if it isn't on.
( $is_conversational_style ? 'options LIKE' : 'options NOT LIKE' ) => ';s:4:"chat";',
$where[0],
);
}
return FrmDb::get_count( 'frm_forms', $where );
}
/**
* Check if the current page is the advanced settings page.
*
* @since 6.14
*
* @return bool True if is advanced settings, false otherwise.
*/
public static function is_advanced_settings() {
return FrmAppHelper::get_param( 'section' ) === 'advanced-settings' && FrmAppHelper::get_param( 'page' ) === 'formidable-styles';
}
/**
* Retrieve the background image URL of the submit button.
* It may be either a full URL string (used in versions prior to 6.14) or a numeric attachment ID (introduced in version 6.14).
*
* @since 6.14
*
* @param array $settings
* @return false|string Return image url or false.
*/
public static function get_submit_image_bg_url( $settings ) {
$background_image = $settings['submit_bg_img'];
if ( empty( $background_image ) ) {
return false;
}
// Handle the case where the submit_bg_img is a full URL string. If the settings were saved with the older styler version prior to 6.14, the submit_bg_img will be a full URL string.
if ( ! is_numeric( $background_image ) ) {
return $background_image;
}
return wp_get_attachment_url( (int) $background_image );
}
/**
* Determines if the chosen JavaScript library should be used.
*
* @since 6.13
*
* @return bool
*/
public static function use_chosen_js() {
if ( ! FrmAppHelper::pro_is_installed() ) {
return false;
}
return is_callable( 'FrmProAppHelper::use_chosen_js' )
? FrmProAppHelper::use_chosen_js()
: true;
}
/**
* @since 5.5.1
* @deprecated 6.10
* @return void
*/
public static function maybe_include_font_icon_css() {
_deprecated_function( __METHOD__, '6.10' );
}
}