/**
* 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 `