/** * Custom DTX Allowed Protocols Filter * * @since 3.3.0 * * @param array|string $protocols Optional. Specify protocols to allow either as an array of string values or a string value of comma separated protocols. * @param bool $replace Optional. If true, this function will only return the specified values. If false, will merge specified values with default values. Default is false. * * @return array An array of string values, default only includes `http` and `https` protocols. */ function wpcf7dtx_allow_protocols($protocols = false, $replace = false) { // Get user-inputted protocols $user_protocols = false; if (is_string($protocols) && !empty($protocols)) { $user_protocols = explode(',', sanitize_text_field($protocols)); } elseif (is_array($protocols) && count($protocols)) { $user_protocols = array_filter(array_values($protocols)); } $default = array('http', 'https'); if (is_array($user_protocols) && count($user_protocols)) { // Sanitize each value before adding $allowed_protocols = array(); foreach ($user_protocols as $protocol) { $allowed_protocols[] = sanitize_text_field($protocol); } if ($replace) { return array_unique($allowed_protocols); } return array_unique(array_merge(array('http', 'https'), $allowed_protocols)); // Return merged values } elseif ($replace) { return array(); // None allowed, apparently } return $default; // Return only default values } add_filter('wpcf7dtx_allow_protocols', 'wpcf7dtx_allow_protocols', 10, 2); /** * Custom DTX Sanitize Filter * * @since 3.3.0 * * @param string $value value to be sanitized * @param string $type Optional. The type of sanitation to return. Default is `auto` where automatic identification will be used to attempt to identify URLs and email addresses vs text. * @param array|string $protocols Optional. Specify protocols to allow either as an array of string values or a string value of comma separated protocols. * * @return string the sanitized value */ function wpcf7dtx_sanitize($value = '', $type = 'auto', $protocols = false) { if ($type == 'none') { return $value; } $value = is_string($value) ? $value : strval($value); // Force string value if (!empty($value)) { $type = $type == 'auto' ? wpcf7dtx_detect_value_type($value) : sanitize_text_field($type); switch ($type) { case 'email': return sanitize_email($value); case 'url': return sanitize_url($value, apply_filters('wpcf7dtx_allow_protocols', $protocols)); case 'key': return sanitize_key($value); case 'slug': return sanitize_title($value); case 'textarea': return sanitize_textarea_field($value); } } return sanitize_text_field($value); } add_filter('wpcf7dtx_sanitize', 'wpcf7dtx_sanitize', 10, 3); /** * Custom DTX Escape Filter * * @since 3.3.0 * * @param string $value value to be escaped * @param bool $obfuscate Optional. If true, returned value will be obfuscated. Default is false. * @param string $type Optional. The type of escape to return. Default is `auto` where automatic identification will be used to attempt to identify the type of text. * @param array|string $protocols Optional. Specify protocols to allow either as an array of string values or a string value of comma separated protocols. * * @return string the escaped value */ function wpcf7dtx_escape($value = '', $obfuscate = false, $type = 'auto', $protocols = false) { if ($type == 'none') { return $value; } $value = apply_filters('wpcf7dtx_sanitize', $value, $type); // Sanitize value if (!empty($value)) { if ($obfuscate) { return apply_filters('wpcf7dtx_obfuscate', $value); // Return obfuscated value } $type = $type == 'auto' ? wpcf7dtx_detect_value_type($value) : sanitize_text_field($type); switch ($type) { case 'url': return esc_url($value, apply_filters('wpcf7dtx_allow_protocols', $protocols)); case 'textarea': return esc_textarea($value); } } return esc_attr($value); // Return attribute value } add_filter('wpcf7dtx_escape', 'wpcf7dtx_escape', 10, 4); /** * Detect Value Type * * @since 3.3.0 * * @access private * * @param string $value the value to be identified * * @return string Potentially identifies string values as `url`, `email`, or `text`. */ function wpcf7dtx_detect_value_type($value) { // Try to detect the value type $value = trim($value); $is_https_url = stripos($value, 'https') === 0 && strlen($value) > 5; $is_http_url = stripos($value, 'http') === 0 && strlen($value) > 4 && sanitize_key($value) != 'https'; if ($is_https_url || $is_http_url) { return 'url'; } elseif (preg_match('/^[^\s@]+@[^\s@]+\.[^\s@]+$/', $value)) { return 'email'; } return 'text'; } /** * Obfuscate a value * * @see https://aurisecreative.com/docs/contact-form-7-dynamic-text-extension/shortcodes/dtx-attribute-obfuscate/ * * @param mixed $value the value to be obfuscated * * @return string obfuscated value */ function wpcf7dtx_obfuscate($value = '') { $o = ''; if (!is_string($value)) { $value = sanitize_text_field(strval($value)); // Force value to be string and sanitize it } if (!empty($value)) { $value = htmlentities($value, ENT_QUOTES); foreach (str_split($value) as $letter) { $o .= '&#' . ord($letter) . ';'; } return $o; // Return obfuscated value } return esc_attr($value); // Return default attribute escape } add_filter('wpcf7dtx_obfuscate', 'wpcf7dtx_obfuscate', 10, 1); /** * Get Post ID * * @access private * * @param mixed $post_id * * @return int An integer value of the passed post ID or the post ID of the current `$post` global object. 0 on Failure. */ function wpcf7dtx_get_post_id($post_id, $context = 'dtx') { $post_id = $post_id ? intval(sanitize_text_field(strval($post_id))) : ''; if (!$post_id || !is_numeric($post_id)) { if ($context == 'dtx') { if (wp_doing_ajax()) { // If we're doing an AJAX call, just fail quietly return 0; } else { global $post; if (isset($post)) { $post_id = $post->ID; // If the global $post object is set, get its ID } else { $post_id = get_the_ID(); // Otherwise get it from "the loop" } } } elseif ($context == 'acf') { // When a post ID is not specified for ACF keys, it accepts the boolean `false` $post_id = false; } } return $post_id; } /** * Get Dynamic Value * * @since 3.2.2 * * @param string $value The form tag value. * @param WPCF7_FormTag|false $tag Optional. Use to look up default value. * @param string $sanitize Optional. Specify type of sanitization. Default is `auto`. * @param string $option_name Optional. Specify an option from the $tag to retrieve and decode. Default is `value`. * @param string $option_pattern Optional. A regular expression pattern or one of the keys of preset patterns. If specified, only options whose value part matches this pattern will be returned. * * @return string The dynamic output or the original value, not escaped or sanitized. */ function wpcf7dtx_get_dynamic($value, $tag = false, $sanitize = 'auto', $option_name = 'value', $option_pattern = '') { if (is_object($tag)) { if ($option_name != 'value') { $value = html_entity_decode(urldecode(strval($tag->get_option($option_name, $option_pattern, true))), ENT_QUOTES); } else { $default = $tag->get_option('defaultvalue', '', true); if (!$default) { $default = $tag->get_default_option(strval(reset($tag->values))); } $value = wpcf7_get_hangover($tag->name, $default); } } $value = apply_filters('wpcf7dtx_sanitize', $value, $sanitize); if (is_string($value) && !empty($value)) { // If a shortcode was passed as the value, attempt to evaluate itevaluate it and use the result $shortcode_tag = '[' . $value . ']'; //var_dump('Shortcode tag?', $shortcode_tag); $shortcode_output = do_shortcode($shortcode_tag); //Shortcode value //var_dump('Shortcode value?', $shortcode_output); if ($shortcode_output != $shortcode_tag) { return apply_filters('wpcf7dtx_sanitize', $shortcode_output, $sanitize); } } return $value; } /** * Get Allowed HTML for Form Field Properties * * @see https://www.w3schools.com/tags/tag_input.asp * @see https://www.w3schools.com/tags/tag_optgroup.asp * @see https://www.w3schools.com/tags/tag_option.asp * @see https://www.w3schools.com/tags/tag_select.asp * @see https://www.w3schools.com/tags/tag_textarea.asp * * @since 4.0.0 * * @param string $type Optional. The type of input for unique properties. Default is `text`. * @param array $extra Optional. A sequential array of properties to additionally include. * * @return array An associative array of allowed properties appropriate for use in `wp_kses()` */ function wpcf7dtx_get_allowed_field_properties($type = 'text', $extra = array()) { if (in_array($type, array('option', 'optgroup'))) { return array( 'optgroup' => array( 'label' => array(), 'disabled' => array(), 'hidden' => array() ), 'option' => array( 'value' => array(), 'selected' => array(), 'disabled' => array(), 'hidden' => array() ) ); } $allowed_properties = array( // Global properties 'type' => array(), 'id' => array(), 'name' => array(), 'value' => array(), 'class' => array(), 'disabled' => array(), 'tabindex' => array(), 'title' => array(), // ARIA properties 'aria-invalid' => array(), 'aria-describedby' => array(), // DTX properties 'data-dtx-value' => array(), ); if ($type != 'hidden') { $allowed_properties['autofocus'] = array(); $allowed_properties['readonly'] = array(); $allowed_properties['required'] = array(); } if (in_array($type, array('checkbox', 'radio', 'acceptance'))) { // Properties exclusive to checkboxes and radio buttons $allowed_properties['checked'] = array(); $allowed_properties['dtx-default'] = array(); } elseif ($type == 'select') { // Properties exclusive to select fields $allowed_properties['size'] = array(); $allowed_properties['multiple'] = array(); $allowed_properties['dtx-default'] = array(); unset($allowed_properties['type'], $allowed_properties['value']); // Remove invalid select attributes } elseif ($type == 'label') { // Properties exclusive to label elements $allowed_properties['for'] = array(); // Remove invalid label attributes unset( $allowed_properties['type'], $allowed_properties['name'], $allowed_properties['value'], $allowed_properties['disabled'], $allowed_properties['aria-invalid'] ); } else { // Properties exclusive to text-based inputs $allowed_properties['autocapitalize'] = array(); $allowed_properties['autocomplete'] = array(); $allowed_properties['list'] = array(); // Placeholder if (in_array($type, array('text', 'search', 'url', 'tel', 'email', 'password', 'number'))) { $allowed_properties['placeholder'] = array(); } // Textarea if ($type == 'textarea') { // Additional properties exclusive to textarea fields $allowed_properties['cols'] = array(); $allowed_properties['rows'] = array(); $allowed_properties['minlength'] = array(); $allowed_properties['maxlength'] = array(); $allowed_properties['wrap'] = array(); unset($allowed_properties['type'], $allowed_properties['value']); // Remove invalid textarea attributes } elseif (in_array($type, array('text', 'search', 'url', 'tel', 'email', 'password'))) { // Additional properties exclusive to these text-based fields $allowed_properties['size'] = array(); $allowed_properties['minlength'] = array(); $allowed_properties['maxlength'] = array(); $allowed_properties['pattern'] = array(); } elseif (in_array($type, array('number', 'range', 'date', 'datetime-local', 'time'))) { // Number and date inputs $allowed_properties['min'] = array(); $allowed_properties['max'] = array(); $allowed_properties['step'] = array(); } } if (is_array($extra) && count($extra)) { foreach ($extra as $property) { $allowed_properties[sanitize_title($property)] = array(); } } return $allowed_properties; } /** * Returns a formatted string of HTML attributes * * @since 4.0.0 * * @param array $atts Associative array of attribute name and value pairs * * @return string Formatted HTML attributes with keys and values both escaped */ function wpcf7dtx_format_atts($atts) { if (is_array($atts) && count($atts)) { $sanitized_atts = array(); static $boolean_attributes = array( 'checked', 'disabled', 'multiple', 'readonly', 'required', 'selected' ); foreach ($atts as $key => $value) { $key = sanitize_key(strval($key)); if ($key) { if (in_array($key, $boolean_attributes) || is_bool($value)) { if ($value) { $sanitized_atts[$key] = $key; } } elseif (is_numeric($value) || (is_string($value) || !empty($value))) { $sanitized_atts[$key] = $value; } } } if (count($sanitized_atts)) { $output = array(); foreach ($sanitized_atts as $key => $value) { $output[] = sprintf('%s="%s"', esc_attr($key), esc_attr($value)); } return implode(' ', $output); } } return ''; } /** * Create Input Field HTML * * @since 4.0.0 * * @param array $atts An associative array of input attributes. * * @return string HTML output of input field */ function wpcf7dtx_input_html($atts) { return sprintf('', wpcf7dtx_format_atts($atts)); } /** * Create Checkbox Field HTML * * @since 4.0.0 * * @param array $atts An associative array of select input attributes. * @param string $label_text Optional. The text to display next to the checkbox or radio button. * @param bool $label_ui Optional. If true, will place input and label text inside a `