{$testi_eot['desc_all_ext']}
EOT;
$required_checked = empty($istance_data['required']) ? '' : 'checked="checked"';
$required = <<
EOT;
// Custom required message override (checkbox + text input, same pattern as custom label)
$custom_required_msg_label = __( 'Custom required message', 'contact-forms');
// translators: %s is the field name/label
$custom_required_msg_desc = __( 'Overrides the default "required" error message. Use %s for the field name.', 'contact-forms');
$custom_required_msg = <<
{$override_begin}{$override_end}
{$custom_required_msg_desc}
EOT;
// Custom format message override for email and telephone fields
$custom_format_msg = '';
if ($field_data['type'] === 'email' || $field_data['type'] === 'autoreply_email') {
$custom_format_msg_label = __( 'Custom invalid email message', 'contact-forms');
// translators: %s is the field name/label
$custom_format_msg_desc = __( 'Overrides the default email format error message. Use %s for the field name.', 'contact-forms');
$custom_format_msg = <<
{$override_begin}{$override_end}
{$custom_format_msg_desc}
EOT;
} elseif ($field_data['type'] === 'telephone') {
$custom_format_msg_label = __( 'Custom invalid phone message', 'contact-forms');
// translators: %s is the field name/label
$custom_format_msg_desc = __( 'Overrides the default phone format error message. Use %s for the field name.', 'contact-forms');
$custom_format_msg = <<
{$override_begin}{$override_end}
{$custom_format_msg_desc}
EOT;
}
if ($field_data['type'] == 'date'){
$default_date_value = <<
{$override_begin}{$override_end}
EOT;
$min_date = <<
{$override_begin}{$override_end}
EOT;
$max_date = <<
{$override_begin}{$override_end}
EOT;
}
// Post type selector for post-select and post-multicheckbox fields
$post_type_selector = '';
if ($field_data['type'] === 'post-select' || $field_data['type'] === 'post-multicheckbox') {
$override_post_type = isset($istance_data['post_type']) && $istance_data['post_type'] !== 'page' ? 'checked="checked"' : '';
$post_types = accua_forms_get_public_post_types();
$post_type_options = '';
$current_post_type = esc_attr($istance_data['post_type']);
foreach ($post_types as $pt_slug => $pt_label) {
$selected = ($pt_slug === $current_post_type) ? ' selected="selected"' : '';
$post_type_options .= '';
}
$post_type_label = __('Post type', 'contact-forms');
$post_type_desc = __('Select which post type to show in the dropdown.', 'contact-forms');
$query_params_label = __('Additional query parameters', 'contact-forms');
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- This is example help text, not actual code.
$query_params_desc = __('Optional: Filter posts using query parameters (e.g., authors=admin or meta_key=featured&meta_value=1). Add post_status=publish,private to also include private posts (their titles become visible to all visitors of this form). Leave empty for all published posts of the selected type.', 'contact-forms');
$post_type_selector = <<
{$override_begin}{$override_end}
{$post_type_desc}
{$override_begin}{$override_end}
{$query_params_desc}
EOT;
}
// Country selector for telephone fields (for libphonenumber validation)
$country_selector = '';
if ($field_data['type'] === 'telephone') {
$countries = accua_forms_get_countries();
$current_country = isset($istance_data['country_code']) ? esc_attr($istance_data['country_code']) : 'IT';
$override_country = isset($istance_data['country_code']) && $istance_data['country_code'] !== 'IT' ? 'checked="checked"' : '';
$country_options = '';
foreach ($countries as $code => $country_name) {
$selected = ($code === $current_country) ? ' selected="selected"' : '';
$country_options .= '';
}
$country_label = __('Default country', 'contact-forms');
// translators: Help text for phone field country selector in form editor
$country_desc = __('For numbers without international prefix, validation assumes this country.', 'contact-forms');
$country_selector = <<
{$override_begin}{$override_end}
{$country_desc}
EOT;
}
// Spam action selector for reCAPTCHA v2 and v3 fields (silent classification).
// Like every other field setting it is an override of the site-wide default
// configured in the plugin settings page.
$spam_action_selector = '';
if ($field_data['type'] === 'captcha' || $field_data['type'] === 'captcha_v3') {
$spam_actions = accua_forms_captcha_spam_action_options();
$current_spam_action = accua_forms_captcha_spam_action($istance_data, $field_data['type']);
$override_spam_action = isset($istance_data['spam_action']) ? 'checked="checked"' : '';
$spam_action_options = '';
foreach ($spam_actions as $sa_key => $sa_label) {
$sa_selected = ($sa_key === $current_spam_action) ? ' selected="selected"' : '';
$spam_action_options .= '';
}
$spam_action_label = __('When the spam check fails', 'contact-forms');
if ($field_data['type'] === 'captcha_v3') {
// translators: Help text for the reCAPTCHA v3 spam action selector in the form editor
$spam_action_desc = __('reCAPTCHA v3 classifies visitors silently with a score. Choose what happens to a submission that fails the check: accept it silently (normal success message, no notification emails) and mark it with the Spam lead status, move it to Trash, or delete it immediately without storing anything, or reject it with a visible error.', 'contact-forms');
} else {
// translators: Help text for the reCAPTCHA v2 spam action selector in the form editor
$spam_action_desc = __('Choose what happens to a submission whose reCAPTCHA verification fails: accept it silently (normal success message, no notification emails) and mark it with the Spam lead status, move it to Trash, or delete it immediately without storing anything, or reject it with a visible error so the visitor can retry the challenge.', 'contact-forms');
}
$spam_action_selector = <<
{$override_begin}{$override_end}
{$spam_action_desc}
EOT;
}
// Minimum score override for reCAPTCHA v3 fields
$score_threshold_field = '';
if ($field_data['type'] === 'captcha_v3') {
$current_score_threshold = esc_attr(number_format(accua_forms_recaptcha3_score_threshold($istance_data), 2, '.', ''));
$override_score_threshold = isset($istance_data['score_threshold']) ? 'checked="checked"' : '';
$score_threshold_label = __('Minimum score', 'contact-forms');
// translators: Help text for the reCAPTCHA v3 minimum score field in the form editor
$score_threshold_desc = __('reCAPTCHA v3 scores every visitor from 0.0 (almost certainly a bot) to 1.0 (almost certainly a person). Submissions scoring below this value fail the spam check. Google suggests 0.5; raise it to be stricter, lower it if legitimate visitors are being caught.', 'contact-forms');
$score_threshold_field = <<
{$override_begin}{$override_end}
{$score_threshold_desc}
EOT;
}
// Hide-title override for captcha fields
$hide_title_field = '';
if (in_array($field_data['type'], array('captcha', 'captcha_v3', 'cap'), true)) {
$hide_title_checked = accua_forms_captcha_hide_title($istance_data) ? 'checked="checked"' : '';
$override_hide_title = isset($istance_data['hide_title']) ? 'checked="checked"' : '';
$hide_title_label = __('Hide field title', 'contact-forms');
// translators: Help text for the hide-title checkbox on captcha fields in the form editor
$hide_title_desc = __('Do not display the field title in the form. The title stays in the markup for screen readers and error messages.', 'contact-forms');
$hide_title_field = <<
{$override_begin}{$override_end}
{$hide_title_desc}
EOT;
}
// CSS Class and CSS ID fields (universal, apply to all field types)
$css_class_label = __( 'CSS Class', 'contact-forms');
$css_id_label = __( 'CSS ID', 'contact-forms');
// translators: Help text for CSS Class field in form editor
$css_class_desc = __( 'Custom CSS class(es) for the field wrapper. Separate multiple classes with spaces.', 'contact-forms');
// translators: Help text for CSS ID field in form editor
$css_id_desc = __( 'Custom CSS ID for the field wrapper. Must be unique on the page.', 'contact-forms');
$css_class_field = <<
{$css_class_desc}
EOT;
$css_id_field = <<
{$css_id_desc}
EOT;
switch ($field_data['type']) {
case 'textarea':
$content .= <<
{$override_begin}{$override_end}
$required
$custom_required_msg
EOT;
break;
case 'hidden':
$content = $default_value;
break;
case 'checkbox':
$content .= $default_value . $required . $custom_required_msg;
break;
case 'select':
case 'radio':
$content .= $default_value . $allowed_values . $required . $custom_required_msg;
break;
case 'post-select':
$content .= $default_value . $post_type_selector . $required . $custom_required_msg;
break;
case 'multiselect':
case 'multicheckbox':
$content .= $default_values . $allowed_values . $required . $custom_required_msg;
break;
case 'post-multicheckbox':
$content .= $default_values . $post_type_selector . $required . $custom_required_msg;
break;
case 'file':
$content .= $allowed_ext . $required . $custom_required_msg;
break;
case 'submit':
//just the label
break;
case 'fieldset-begin':
$fs_label_text = __('Border and Title', 'contact-forms');
$gt_label_text = __('Group Title', 'contact-forms');
// translators: Help text under the Group Title field for fieldset groups in the form editor
$gt_desc_text = __('Section heading. Shown in the form when a title option is selected.', 'contact-forms');
$fs_opts_map = array(
'border-off-title-off' => __('Border OFF | Title OFF', 'contact-forms'),
'border-on-title-off' => __('Border ON | Title OFF', 'contact-forms'),
'border-on-title-inline' => __('Border ON | Title ON (inline)', 'contact-forms'),
'border-on-title-outside' => __('Border ON | Title ON (outside)', 'contact-forms'),
'border-on-title-inside' => __('Border ON | Title ON (inside)', 'contact-forms'),
'border-off-title-on' => __('Border OFF | Title ON', 'contact-forms'),
);
$fs_options_html = '';
foreach ($fs_opts_map as $opt_val => $opt_label) {
$opt_selected = ($istance_data['fieldset_style'] === $opt_val) ? ' selected="selected"' : '';
$fs_options_html .= '';
}
$content = <<
{$gt_desc_text}
EOT;
$content .= $css_class_field . $css_id_field;
break;
case 'fieldset-end':
//Nothing!
$content = '';
break;
case 'html':
$content = <<
{$override_begin}{$override_end}
EOT;
break;
case 'date':
$content .= $default_date_value . $min_date . $max_date . $required . $custom_required_msg;
break;
case 'telephone':
$content .= $default_value . $country_selector . $required . $custom_required_msg . $custom_format_msg;
break;
case 'email':
case 'autoreply_email':
$content .= $default_value . $required . $custom_required_msg . $custom_format_msg;
break;
case 'captcha':
case 'captcha_v3':
case 'cap':
case 'turnstile':
// Captcha fields consume neither default_value nor the custom required
// message (their validators are set in the element constructors), so
// those rows are not offered. The extension-settings action still fires
// for parity with the default case.
ob_start();
do_action( 'accua_forms_field_settings', $field_data['type'], $field_data, $istance_data );
$extra_settings = ob_get_clean();
$content .= $extra_settings . $hide_title_field . $score_threshold_field . $spam_action_selector . $required;
break;
case 'textfield':
case 'colorpicker':
case 'datepicker':
case 'dateselect':
default:
/**
* Action to render additional field settings in the form editor.
*
* @param string $field_type The field type identifier.
* @param array $field_data The field definition.
* @param array $istance_data The field instance data.
* @param string $content The current settings HTML (passed by reference via output buffering).
*/
ob_start();
do_action( 'accua_forms_field_settings', $field_data['type'], $field_data, $istance_data );
$extra_settings = ob_get_clean();
// The captcha-only rows (hide title, score, spam action) render in the
// dedicated captcha case above and are always empty here.
$content .= $default_value . $extra_settings . $required . $custom_required_msg;
break;
}
// Append CSS Class and CSS ID fields to all types except fieldset-end (which has no settings)
if ($field_data['type'] !== 'fieldset-end' && $field_data['type'] !== 'fieldset-begin') {
$content .= $css_class_field . $css_id_field;
}
$adminurl = admin_url();
return <<
{$field_data['name']}
EOT;
// phpcs:enable PluginCheck.CodeAnalysis.Heredoc.NotAllowed, WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
}
// _accua_forms_get_abs_dest_path() now lives in includes/data-deletion.php, which
// uninstall.php requires on its own - the deletion needs the same path this file
// uploads to, and uninstall runs with the plugin unloaded.
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function, underscore prefix indicates private
function _accua_forms_get_form_data($fid = false, $return_empty = true, $restore_trash = false){
$empty_form_data = array(
'fields' => array(),
'title' => '',
'success_message' => '',
'error_message' => '',
'emails_from_name' => '',
'emails_from' => '',
'admin_emails_to' => '',
'emails_bcc' => '',
'admin_emails_subject' => '',
'admin_emails_message' => '',
'confirmation_emails_subject' => '',
'confirmation_emails_message' => '',
'use_ajax' => true,
'gads_conversion_tracking_code' => '',
'layout' => 'sidebyside',
'style_margin' => '',
'style_border_color' => '',
'style_border_width' => '',
'style_border_radius' => '',
'style_background_color' => '',
'style_padding' => '',
'style_color' => '',
'style_font_size' => '',
'style_field_spacing' => '',
'style_field_border_color' => '',
'style_field_border_width' => '',
'style_field_border_radius' => '',
'style_field_background_color' => '',
'style_field_padding' => '',
'style_field_color' => '',
'style_submit_border_color' => '',
'style_submit_border_width' => '',
'style_submit_border_radius' => '',
'style_submit_background_color' => '',
'style_submit_padding' => '',
'style_submit_color' => '',
'style_submit_font_size' => '',
'submission_retention_override' => false,
'submission_retention_value' => 0,
'submission_retention_unit' => 'months',
'submission_retention_mode' => 'anonymize',
);
if ($fid === false) {
return $empty_form_data;
}
$default_form_data = get_option('accua_forms_default_form_data',array());
if ($fid === null) {
return $default_form_data + $empty_form_data;
}
$forms_data = get_option('accua_forms_saved_forms', array());
if ($restore_trash) {
$trash_data = get_option('accua_forms_trash_forms', array());
if (isset($trash_data[$fid])) {
$forms_data[$fid] = $trash_data[$fid];
update_option('accua_forms_saved_forms', $forms_data);
unset($trash_data[$fid]);
update_option('accua_forms_trash_forms', $trash_data);
}
}
if (isset($forms_data[$fid])) {
$form_data = array(
'_overrided' => $forms_data[$fid]
) + $forms_data[$fid] + $default_form_data + $empty_form_data;
return $form_data;
} elseif ($return_empty) {
return array(
'_overrided' => array()
) + $default_form_data + $empty_form_data;
} else {
return null;
}
}
/**
* Draft System Functions
*
* The draft system allows users to make changes to forms in the admin editor
* without immediately affecting the live/published form. Changes are stored in
* a transient until the user clicks Save, which publishes the draft.
*
* Pattern follows WordPress auto-draft system.
*/
/**
* Get the transient key for a form's draft data.
*
* @param string|int $fid Form ID.
* @return string Transient key.
*/
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function
function _accua_forms_get_draft_key( $fid ) {
return 'accua_forms_draft_' . $fid;
}
/**
* Initialize or get existing draft for a form.
* Called when the form editor is loaded.
*
* If a draft exists, returns it.
* If no draft exists, creates one from published data.
*
* @param string|int $fid Form ID.
* @return array Draft data array.
*/
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function
function _accua_forms_init_draft( $fid ) {
$draft_key = _accua_forms_get_draft_key( $fid );
// Check for existing draft
$draft_data = get_transient( $draft_key );
if ( $draft_data !== false ) {
// Draft exists - return it
return $draft_data;
}
// No draft - create from published data
$forms_data = get_option( 'accua_forms_saved_forms', array() );
if ( isset( $forms_data[ $fid ] ) ) {
$draft_data = $forms_data[ $fid ];
} else {
// New form - initialize empty structure
$draft_data = array( 'fields' => array() );
}
// Store as draft with 24 hour expiry
set_transient( $draft_key, $draft_data, DAY_IN_SECONDS );
return $draft_data;
}
/**
* Get draft data for a form (creating if necessary).
* Used by AJAX handlers to read current draft state.
*
* @param string|int $fid Form ID.
* @return array Draft data array.
*/
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function
function _accua_forms_get_draft_data( $fid ) {
$draft_key = _accua_forms_get_draft_key( $fid );
$draft_data = get_transient( $draft_key );
if ( $draft_data === false ) {
// Initialize draft from published data
$draft_data = _accua_forms_init_draft( $fid );
}
return $draft_data;
}
/**
* Save data to draft transient.
* Called by AJAX handlers when fields are edited.
*
* @param string|int $fid Form ID.
* @param array $draft_data Complete draft data to save.
* @return bool True on success.
*/
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function
function _accua_forms_save_draft( $fid, $draft_data ) {
$draft_key = _accua_forms_get_draft_key( $fid );
return set_transient( $draft_key, $draft_data, DAY_IN_SECONDS );
}
/**
* Publish draft to live data.
* Called when user clicks Save button.
*
* @param string|int $fid Form ID.
* @return bool True on success.
*/
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function
function _accua_forms_publish_draft( $fid ) {
$draft_key = _accua_forms_get_draft_key( $fid );
$draft_data = get_transient( $draft_key );
if ( $draft_data === false ) {
// No draft to publish - shouldn't happen normally
return false;
}
// Get current published data
$forms_data = get_option( 'accua_forms_saved_forms', array() );
// Update with draft
$forms_data[ $fid ] = $draft_data;
// Save to database
$result = update_option( 'accua_forms_saved_forms', $forms_data );
if ( $result ) {
// Clear draft after successful publish
delete_transient( $draft_key );
}
return $result;
}
/**
* Delete draft for a form.
* Called when discarding changes or after successful publish.
*
* @param string|int $fid Form ID.
* @return bool True on success.
*/
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function
function _accua_forms_delete_draft( $fid ) {
$draft_key = _accua_forms_get_draft_key( $fid );
return delete_transient( $draft_key );
}
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function, underscore prefix indicates private
function _accua_forms_style_parameters($params) {
$ret = '';
foreach ($params as $key => $value) {
$value = trim($value);
if ($value !== '') {
if (is_numeric($value)) {
$ret .= "{$key}:{$value}px;";
} else {
$ret .= "{$key}:{$value};";
}
if ($key == 'border-width') {
$ret .= "border-style:solid;";
}
}
}
return $ret;
}
/*
* generazione del form (e della preview)
* */
add_action('accua_form_alter', 'accua_forms_form_generate', -999, 2);
function accua_forms_form_generate($baseid, $form) {
if (substr($baseid, 0, 14) == '__accua-form__') {
$fid = substr($baseid,14);
// Check if we're in admin preview mode - if so, read from draft
$use_draft = apply_filters('accua_forms_use_draft_for_preview', false);
if ($use_draft) {
// Get draft data and merge with defaults
$draft_data = _accua_forms_get_draft_data($fid);
$default_form_data = get_option('accua_forms_default_form_data', array());
$empty_form_data = _accua_forms_get_form_data(false); // Get empty structure
$form_data = array(
'_overrided' => $draft_data
) + $draft_data + $default_form_data + $empty_form_data;
} else {
// Frontend: read from published data
$form_data = _accua_forms_get_form_data($fid, false);
}
/*
echo '";
*/
if ($form_data) {
// Check for preview order override (allows live preview of field reorder before save)
$preview_order_override = apply_filters('accua_forms_preview_order_override', null);
if ($preview_order_override && !empty($form_data['fields'])) {
// Find the sidebar key for this form (format: cimatti-accua-fields-form-area-{fid})
$sidebar_key = 'cimatti-accua-fields-form-area-' . $fid;
if (isset($preview_order_override[$sidebar_key])) {
$order_string = $preview_order_override[$sidebar_key];
$order_array = explode(',', $order_string);
// Reorder fields according to preview order
$old_fields = $form_data['fields'];
$new_fields = array();;
foreach ($order_array as $widget_id) {
// Extract instance ID from widget ID (format: widget-{type}_{instance_id})
$instance_id = preg_replace('/^(new-)?widget-\\d+_/', '', $widget_id);
if (isset($old_fields[$instance_id])) {
$new_fields[$instance_id] = $old_fields[$instance_id];
unset($old_fields[$instance_id]);
}
}
// Append any remaining fields not in order
if ($old_fields) {
$new_fields += $old_fields;
}
$form_data['fields'] = $new_fields;
}
}
$form_style = _accua_forms_style_parameters(array(
'margin' => $form_data['style_margin'],
'border-color' => $form_data['style_border_color'],
'border-width' => $form_data['style_border_width'],
'border-radius' => $form_data['style_border_radius'],
'background-color' => $form_data['style_background_color'],
'padding' => $form_data['style_padding'],
'color' => $form_data['style_color'],
'font-size' => $form_data['style_font_size'],
));
$field_style = _accua_forms_style_parameters(array(
'margin-bottom' => $form_data['style_field_spacing'],
'border-color' => $form_data['style_field_border_color'],
'border-width' => $form_data['style_field_border_width'],
'border-radius' => $form_data['style_field_border_radius'],
'background-color' => $form_data['style_field_background_color'],
'padding' => $form_data['style_field_padding'],
'color' => (trim($form_data['style_field_color']) === '')?$form_data['style_color']:$form_data['style_field_color'],
'font-size' => $form_data['style_font_size'],
));
$field_properties = array();
if ($field_style !== '') {
$field_properties['style'] = $field_style;
}
// These will be set per-field in the loop below, initialized empty here
$field_properties['wrapperCssClass'] = '';
$field_properties['wrapperCssId'] = '';
$submit_style = _accua_forms_style_parameters(array(
'border-color' => $form_data['style_submit_border_color'],
'border-width' => $form_data['style_submit_border_width'],
'border-radius' => $form_data['style_submit_border_radius'],
'background-color' => $form_data['style_submit_background_color'],
'padding' => $form_data['style_submit_padding'],
'color' => $form_data['style_submit_color'],
'font-size' => $form_data['style_submit_font_size'],
));
$submit_properties = array();
if ($submit_style !== '') {
$submit_properties['style'] = $submit_style;
}
if ($form_style !== '') {
$form->configure(array('style' => $form_style));
}
if (!empty($form_data['use_ajax'])){
$form->configure(array(
"accua_ajax" => 1,
));
}
$add_submit = true;
$fieldset_open = false;
$avail_fields = get_option('accua_forms_avail_fields', array());
foreach ($form_data['fields'] as $istance_data) {
if (empty($avail_fields[$istance_data['ref']])) {
$field_data = array();
if (!empty($istance_data['ref'])) {
if ($istance_data['ref'] == '__fieldset-begin') {
$field_data = array(
'id' => '__fieldset-begin',
'name' => __('Fieldset begin', 'contact-forms'),
'type' => 'fieldset-begin',
'description' => '',
);
} elseif ($istance_data['ref'] == '__fieldset-end') {
$field_data = array(
'id' => '__fieldset-end',
'name' => __('Fieldset end', 'contact-forms'),
'type' => 'fieldset-end',
'description' => '',
);
}
}
} else {
$field_data = $avail_fields[$istance_data['ref']];
}
$field_data += array(
'version' => 1,
'id' => '__html',
'name' => __( 'Custom HTML content', 'contact-forms'),
'type' => 'html',
'description' => __('Use this special field to inject raw HTML in the form. You can use this multiple times.', 'contact-forms'),
'default_value' => '',
'allowed_values' => '',
'allowed_extensions' => '',
'custom_required_message' => '',
'custom_format_message' => '',
);
$istance_data += array(
'version' => 1,
);
if ($field_data['type'] == 'file') {
if ($field_data['version'] < 2) {
$field_data['allowed_extensions'] = $field_data['allowed_values'];
}
if ($istance_data['version'] < 2 && isset($istance_data['allowed_values'])) {
$istance_data['allowed_extensions'] = $istance_data['allowed_values'];
}
}
/* prendo i dati relativi ai campi di tipo date
lo faccio prima, così default value in questo modo viene sovrascritto se un campo è di un tipo diverso
NB: se i campi sono già sovrascritti, si trovano già in istance_data */
if ($field_data['type'] == 'date'){
$istance_data += array(
'default_value' => $field_data['default_date_value'],
'min_date' => $field_data['min_date'],
'max_date' => $field_data['max_date'],
);
}
$istance_data += array(
'istance_id' => $field_data['id'],
'widget_number' => '',
'ref' => $field_data['id'],
'label' => $field_data['name'],
'default_value' => $field_data['default_value'], /* viene impostato il valore di defualt se non è un campo data */
'allowed_values' => $field_data['allowed_values'],
'allowed_extensions' => $field_data['allowed_extensions'],
'post_type' => 'page', // Default for post-select fields
'css_class' => '',
'css_id' => '',
'custom_required_message' => '',
'custom_format_message' => '',
);
$element = NULL;
$element_conf = NULL;
$allowed_val = trim($istance_data['allowed_values']);
// For post-select fields, we use lazy loading via AJAX, so don't pre-load posts here
if ($field_data['type'] == 'post-multicheckbox') {
// Post-multicheckbox still needs pre-loaded options for checkbox rendering
$post_type = isset($istance_data['post_type']) ? $istance_data['post_type'] : 'page';
$query_args = array();
if (!empty($allowed_val)) {
wp_parse_str($allowed_val, $query_args);
}
// Let a post_type in the query parameters override the field's
// post-type setting, matching post-select. The value comes from the
// saved field configuration (not a client request) and is validated
// against public post types.
if (!empty($query_args['post_type'])) {
$mc_public_types = get_post_types(array('public' => true));
$mc_override = sanitize_text_field($query_args['post_type']);
if (isset($mc_public_types[$mc_override])) {
$post_type = $mc_override;
}
}
$query_args['post_type'] = $post_type;
// Only publish/private may be exposed, even if the admin configured other statuses.
if (!empty($query_args['post_status'])) {
$mc_statuses = accua_forms_filter_field_post_status($query_args['post_status']);
if (!empty($mc_statuses)) {
$query_args['post_status'] = $mc_statuses;
} else {
unset($query_args['post_status']);
}
}
$posts = accua_get_pages($query_args);
$allowed_values = array();
foreach ($posts as $p) {
$allowed_values[$p->ID] = $p->post_title;
}
} elseif ($field_data['type'] == 'post-select') {
// Post-select uses lazy loading - just set empty options, JS will fetch
$allowed_values = array();
} else {
if ($field_data['type'] == 'file') {
$filedata = get_option('accua_forms_default_file_field_data',array());
$filedata += array(
'valid_extensions' => '',
'max_filesize' => '',
'dest_path' => '',
);
$allowed_val = accua_forms_filter_extensions($istance_data['allowed_extensions']);
if ($allowed_val == '') {
$allowed_val = accua_forms_filter_extensions($filedata['valid_extensions']);
}
}
$allowed_val = explode("\n",$allowed_val);
$allowed_values = array();
foreach ($allowed_val as $val) {
$val = explode('|',$val,2);
$val[0] = trim($val[0]);
/*
if (empty($val[0])) {
continue;
}
*/
if ((!isset($val[1])) || (trim($val[1])==='')) {
if ($val[0] === '') {
continue;
} else {
$val[1] = $val[0];
}
}
$allowed_values[$val[0]] = $val[1];
}
}
// Set per-field wrapper CSS class and ID
$field_properties['wrapperCssClass'] = isset($istance_data['css_class']) ? $istance_data['css_class'] : '';
$field_properties['wrapperCssId'] = isset($istance_data['css_id']) ? $istance_data['css_id'] : '';
// Captcha fields hide their title by default: the wrapper class makes
// the label screen-reader only (frontend.css), keeping it available to
// assistive tech and to the error summary JS label lookup.
if (in_array($field_data['type'], array('captcha', 'captcha_v3', 'cap'), true)
&& accua_forms_captcha_hide_title($istance_data)) {
$field_properties['wrapperCssClass'] = trim($field_properties['wrapperCssClass'] . ' accua-captcha-title-hidden');
}
// Resolve per-field custom validation messages (per-form instance → field definition → default)
$resolved_required_msg = '';
if (!empty($istance_data['custom_required_message'])) {
$resolved_required_msg = $istance_data['custom_required_message'];
} elseif (!empty($field_data['custom_required_message'])) {
$resolved_required_msg = $field_data['custom_required_message'];
}
$resolved_format_msg = '';
if (!empty($istance_data['custom_format_message'])) {
$resolved_format_msg = $istance_data['custom_format_message'];
} elseif (!empty($field_data['custom_format_message'])) {
$resolved_format_msg = $field_data['custom_format_message'];
}
// Add data attributes for client-side custom messages (reset each iteration)
unset($field_properties['data-custom-required-msg']);
unset($field_properties['data-custom-format-msg']);
if ($resolved_required_msg !== '') {
$field_properties['data-custom-required-msg'] = $resolved_required_msg;
}
if ($resolved_format_msg !== '') {
$field_properties['data-custom-format-msg'] = $resolved_format_msg;
}
switch ($field_data['type']) {
case 'textarea':
$element = new Element_Textarea($istance_data['label'], $istance_data['istance_id'], $field_properties+array('cols' => '50', 'value'=>$istance_data['default_value']));
break;
case 'hidden':
// Hidden inputs render without the .pfbc-element wrapper, so the
// editor's CSS Class / CSS ID land on the input itself.
$hidden_props = array();
if ( !empty($field_properties['wrapperCssId']) ) {
$hidden_props['id'] = $field_properties['wrapperCssId'];
}
if ( !empty($field_properties['wrapperCssClass']) ) {
$hidden_props['class'] = $field_properties['wrapperCssClass'];
}
$element = new Element_Hidden($istance_data['istance_id'], $istance_data['default_value'], !empty($hidden_props) ? $hidden_props : null);
break;
case 'checkbox':
$lab = $istance_data['label'];
if (!empty($istance_data['required'])) {
$lab .= ' *';
}
if ($allowed_values) {
reset($allowed_values);
$val = (string) key($allowed_values);
$defval = trim($istance_data['default_value']);
} elseif ($istance_data['default_value'] == '1') {
$defval = $val = '1';
} else {
$val = empty($istance_data['default_value'])?'1':$istance_data['default_value'];
$defval = '';
}
$element = new AccuaForm_Element_Checkbox('', $istance_data['istance_id'], array($val => $lab), $field_properties+array('value' => $defval));
break;
case 'select':
if (!isset($allowed_values[''])) {
$allowed_values = array('' => '') + $allowed_values;
}
$defval = trim($istance_data['default_value']);
$element = new AccuaForm_Element_Select($istance_data['label'], $istance_data['istance_id'], $allowed_values, $field_properties+array('value'=>$defval));
break;
case 'post-select':
$post_type = isset($istance_data['post_type']) ? $istance_data['post_type'] : 'page';
$extra_args = trim($istance_data['allowed_values']);
$defval = trim($istance_data['default_value']);
$element = new AccuaForm_Element_PostSelect($istance_data['label'], $istance_data['istance_id'], $post_type, $extra_args, $field_properties+array('value'=>$defval));
break;
case 'radio':
$defval = trim($istance_data['default_value']);
$element = new AccuaForm_Element_Radio($istance_data['label'], $istance_data['istance_id'], $allowed_values, $field_properties+array('value'=>$defval));
break;
case 'multiselect':
$defval = explode('|', $istance_data['default_value']);
foreach ($defval as $k => $v) {
$defval[$k] = trim($v);
}
$element = new AccuaForm_Element_Select($istance_data['label'], $istance_data['istance_id'], $allowed_values, $field_properties+array('multiple' => true, 'value'=>$defval));
break;
case 'multicheckbox':
case 'post-multicheckbox':
$defval = explode('|', $istance_data['default_value']);
foreach ($defval as $k => $v) {
$defval[$k] = trim($v);
}
$element = new AccuaForm_Element_Checkbox($istance_data['label'], $istance_data['istance_id'], $allowed_values, $field_properties+array('value'=>$defval));
break;
case 'file':
$fdata = array();
if ($allowed_values) {
$fdata['validExtensions'] = array_keys($allowed_values);
}
if (!empty($filedata['max_size'])){
$fdata['maxSize'] = $filedata['max_size'];
}
$fdata['destPath'] = _accua_forms_get_abs_dest_path($filedata['dest_path']);
$element = new AccuaForm_Element_File($istance_data['label'], $istance_data['istance_id'], $field_properties+$fdata);
break;
case 'html':
$element = new Element_HTML($istance_data['default_value'], $field_properties);
break;
case 'email':
case 'autoreply_email':
$email_props = $field_properties+array('value'=>$istance_data['default_value']);
if ($resolved_format_msg !== '') {
$email_props['custom_format_message'] = $resolved_format_msg;
}
$element = new AccuaForm_Element_Email($istance_data['label'], $istance_data['istance_id'], $email_props);
break;
case 'colorpicker':
$element = new AccuaForm_Element_ColorPicker($istance_data['label'], $istance_data['istance_id'], $field_properties+array('value'=>$istance_data['default_value']));
break;
case 'fieldset-begin':
if ($fieldset_open) {
$form->addElement(new AccuaForm_Element_FieldsetEnd());
} else {
$fieldset_open = true;
}
$fs_props = $field_properties;
$fs_props['fieldset_style'] = isset($istance_data['fieldset_style']) ? $istance_data['fieldset_style'] : 'border-off-title-off';
$element = new AccuaForm_Element_FieldsetBegin($istance_data['label'], $istance_data['istance_id'], $fs_props);
break;
case 'fieldset-end':
if ($fieldset_open) {
$element = new AccuaForm_Element_FieldsetEnd();
$fieldset_open = false;
}
break;
case 'submit':
$add_submit = false;
$submit_extra = array('name' => $istance_data['istance_id'], 'value' => $istance_data['default_value']);
// Buttons render inside the shared .pfbc-buttons group without a
// per-element wrapper, so the editor's CSS Class / CSS ID are
// applied to the button element itself.
if (!empty($field_properties['wrapperCssClass'])) {
$submit_extra['class'] = $field_properties['wrapperCssClass'];
}
if (!empty($field_properties['wrapperCssId'])) {
$submit_extra['id'] = $field_properties['wrapperCssId'];
}
$element = new Element_Button($istance_data['label'], 'submit', $submit_properties+$submit_extra);
break;
case 'captcha':
$empty_captcha_data = array(
'recaptcha_force_v1' => '',
'recaptcha_public_key' => '',
'recaptcha_private_key' => '',
);
$captcha_data = get_option('accua_forms_default_captcha_field_data',array()) + $empty_captcha_data;
$captcha_properties = array(
"description" => "",
// Same accuaform_{fid} key format as v3: it identifies this
// form in the request-scoped spam-flag registry shared by the
// captcha validators (AccuaForm_Validation_CaptchaSpam).
'captchaAction' => 'accuaform_' . preg_replace('/[^A-Za-z0-9_]/', '_', $fid),
'spamAction' => accua_forms_captcha_spam_action($istance_data, 'captcha'),
);
$captcha_use_v1 = true;
if (($captcha_data['recaptcha_public_key'] !== '') && ($captcha_data['recaptcha_private_key'] !== '')) {
$captcha_properties['privateKey'] = $captcha_data['recaptcha_private_key'];
$captcha_properties['publicKey'] = $captcha_data['recaptcha_public_key'];
$captcha_use_v1 = $captcha_data['recaptcha_force_v1'];
}
if ($captcha_use_v1) {
$element = new Element_HTML("\n\n\n\n");
} else {
$element = new AccuaForm_Element_Captcha2 ($istance_data['label'], '', $field_properties+$captcha_properties);
}
break;
case 'captcha_v3':
$captcha3_empty_data = array(
'recaptcha_v3_public_key' => '',
'recaptcha_v3_private_key' => '',
);
$captcha3_data = get_option('accua_forms_default_captcha_field_data',array()) + $captcha3_empty_data;
if (($captcha3_data['recaptcha_v3_public_key'] !== '') && ($captcha3_data['recaptcha_v3_private_key'] !== '')) {
$captcha3_properties = array(
'description' => '',
'privateKey' => $captcha3_data['recaptcha_v3_private_key'],
'publicKey' => $captcha3_data['recaptcha_v3_public_key'],
// Distinct action per form, restricted to the characters Google allows
'captchaAction' => 'accuaform_' . preg_replace('/[^A-Za-z0-9_]/', '_', $fid),
'spamAction' => accua_forms_captcha_spam_action($istance_data, 'captcha_v3'),
'scoreThreshold' => accua_forms_recaptcha3_score_threshold($istance_data),
);
$element = new AccuaForm_Element_Captcha3($istance_data['label'], '', $field_properties+$captcha3_properties);
} else {
$element = new Element_HTML("\n\n\n\n");
}
break;
case 'turnstile':
$element = new AccuaForm_Element_Turnstile($istance_data['label'], $istance_data['istance_id'], $field_properties+array("description" => ""));
break;
case 'cap':
$cap_empty_data = array(
'cap_instance_url' => '',
'cap_site_key' => '',
'cap_secret_key' => '',
);
$cap_data = get_option('accua_forms_default_captcha_field_data',array()) + $cap_empty_data;
if (($cap_data['cap_instance_url'] !== '') && ($cap_data['cap_site_key'] !== '') && ($cap_data['cap_secret_key'] !== '')) {
$cap_properties = array(
'description' => '',
'instanceUrl' => $cap_data['cap_instance_url'],
'siteKey' => $cap_data['cap_site_key'],
'secretKey' => $cap_data['cap_secret_key'],
);
$element = new AccuaForm_Element_Cap($istance_data['label'], '', $field_properties+$cap_properties);
} else {
$element = new Element_HTML("\n\n\n\n");
}
break;
case 'password':
$element = new Element_Password($istance_data['label'], $istance_data['istance_id'], $field_properties+array('value'=>$istance_data['default_value']));
break;
case 'password-and-confirm':
$id_2 = "___{$istance_data['istance_id']}___confirmpass";
$element = new Element_Password($istance_data['label'], $istance_data['istance_id'], $field_properties+array('value'=>$istance_data['default_value']));
$element_conf = new Element_Password(__("Confirm password", 'contact-forms'), $id_2, $field_properties+array('value'=>$istance_data['default_value']));
$element_conf_validator = new AccuaForm_Validation_Password();
$element_conf_validator->configure(array('otherPasswordFieldName'=>$istance_data['istance_id']));
$element_conf->setValidation($element_conf_validator);
break;
case 'date':
$element = new AccuaForm_Element_Date($istance_data['label'], $istance_data['istance_id'], $field_properties+array('value'=>$istance_data['default_value'], 'minDate'=>$istance_data['min_date'], 'maxDate'=>$istance_data['max_date']));
break;
case 'telephone':
$phone_country = isset($istance_data['country_code']) ? $istance_data['country_code'] : 'IT';
$phone_props = $field_properties+array('value'=>$istance_data['default_value'], 'country_code'=>$phone_country);
if ($resolved_format_msg !== '') {
$phone_props['custom_format_message'] = $resolved_format_msg;
}
$element = new AccuaForm_Element_Telephone($istance_data['label'], $istance_data['istance_id'], $phone_props);
break;
//case 'textfield':
default:
/**
* Filter to create a custom Element for an external field type.
*
* @param Element|null $element Null by default; return an Element to override.
* @param string $field_type The field type identifier.
* @param array $field_data The field definition from avail_fields.
* @param array $istance_data The field instance data (label, required, etc.).
* @param array $field_properties Common properties (description, shortDesc, etc.).
*/
$element = apply_filters( 'accua_forms_render_field_element', null, $field_data['type'], $field_data, $istance_data, $field_properties );
if ( ! $element ) {
$element = new Element_Textbox($istance_data['label'], $istance_data['istance_id'], $field_properties+array('value'=>$istance_data['default_value']));
}
break;
}
if ($element) {
if (!empty($istance_data['required'])) {
if (!in_array($field_data['type'], array('captcha_v3', 'cap'), true)) {
// captcha_v3 and cap must not get this class: the client-side required check
// would inspect their hidden token input, which is empty until solved
$element->setClass('accuaforms-field-required');
}
if ($field_data['type'] === 'captcha' && empty($captcha_use_v1)) {
//nothing
} elseif ($field_data['type'] === 'captcha_v3') {
//nothing - v3 has its own validation set in the Element constructor
} elseif ($field_data['type'] === 'turnstile') {
//nothing - turnstile has its own validation set in the Element constructor
} elseif ($field_data['type'] === 'cap') {
//nothing - cap has its own validation set in the Element constructor
} elseif ($field_data['type'] === 'password-and-confirm') {
if ($resolved_required_msg !== '') {
$req_msg = str_replace(array('%s', '%element%'), $istance_data['label'], $resolved_required_msg);
} else {
/* translators: Password field required error */
$req_msg = __( 'Password is required', 'contact-forms' );
}
$element->setValidation(new Validation_Required($req_msg));
} else {
if ($resolved_required_msg !== '') {
$req_msg = str_replace(array('%s', '%element%'), $istance_data['label'], $resolved_required_msg);
} else {
/* translators: %element% is the field label, replaced with str_replace() */
// phpcs:ignore WordPress.WP.I18n.MissingTranslatorsComment -- Translators comment is above
$req_msg = str_replace('%element%', $istance_data['label'], __( '%element% is required', 'contact-forms' ));
}
$element->setValidation(new Validation_Required($req_msg));
}
}
if ($elementName = $element->getName()) {
$element->setClass('accuaform-fieldname-'.$elementName);
}
if ($field_data['type']) {
$element->setClass('accuaform-fieldtype-'.$field_data['type']);
}
$form->addElement($element);
if($element_conf!=NULL) {
$form->addElement($element_conf);
$element_conf=NULL;
}
}
}
if ($fieldset_open) {
$form->addElement(new AccuaForm_Element_FieldsetEnd());
$fieldset_open = false;
}
/**
* Filter to suppress the automatically added submit button.
*
* Used by the Fields page live preview, which renders a single field
* with no use for a submit button. An explicit submit field in the
* form is unaffected.
*
* @param bool $suppress False by default.
*/
if ($add_submit && !apply_filters('accua_forms_preview_suppress_auto_submit', false)) {
$form->addElement(new Element_Button(__('Submit', 'contact-forms'), 'submit', $submit_properties));
}
}
}
}
function accua_forms_aggregate_submitted_data(&$replace_map, $params = array()) {
if (empty($params['txt']) && empty($params['html']) && empty($params['json']) && empty($params['email'])) {
$params += array(
'txt' => true,
'html' => true,
'json' => true,
'email' => true,
);
}
if (!empty($params['txt'])) {
$replace_map['__submitted_txt'] = implode("\n",$replace_map['__submitted_txt_raw']);
}
if (!empty($params['html'])) {
$replace_map['__submitted_html'] = implode("\n
\n
",$replace_map['__submitted_html_raw']);
}
if (!empty($params['json'])) {
$replace_map['__submitted_json'] = _accua_forms_json_encode($replace_map['__submitted_json_raw']);
}
if (!empty($params['email'])) {
$replace_map['__autoreply_email'] = implode('; ', $replace_map['__autoreply_email_raw']);
}
}
add_filter('accua_form_validate', 'accua_forms_validation_handler', 10, 4);
function accua_forms_validation_handler($valid, $submittedID, $submittedData, $form){
if (substr($submittedID, 0, 14) == '__accua-form__') {
$fid = substr($submittedID,14);
$form_data = _accua_forms_get_form_data($fid, false);
if ($form_data) {
return apply_filters('accua_forms_validation', $valid, $fid, $submittedData, $form);
}
}
return $valid;
}
/**
* Sanitize a submissions-list column key.
*
* The case-preserving counterpart of sanitize_key(): column keys embed the
* field slug verbatim (`_field_{slug}`), and slugs are case-sensitive, so
* lowercasing would silently merge two fields whose slugs differ only in case.
* Allows the same character set the Fields page accepts for a slug.
*
* @since 2.3.0
* @param string $key Raw column key.
* @return string Sanitized column key.
*/
function accua_forms_sanitize_column_key($key) {
return preg_replace('/[^A-Za-z0-9_\-]/', '', (string) $key);
}
/**
* Allowed captcha spam actions (reCAPTCHA v2 and v3) and their form-editor
* labels.
*
* 'spam' accepts a submission that fails the captcha check silently (the
* visitor sees the normal success message, no emails are sent) and marks it
* with the Spam lead status; 'trash' and 'delete' are the other two silent
* modes. 'reject' blocks the submission with a visible error - the only
* behavior before 2.2.38, and the reCAPTCHA v2 default again since 2.3.0.
*
* @return array action key => translated label.
*/
function accua_forms_captcha_spam_action_options() {
return array(
'spam' => __('Accept silently and mark as Spam', 'contact-forms'),
'trash' => __('Accept silently and move to Trash', 'contact-forms'),
'delete' => __('Accept silently and delete immediately', 'contact-forms'),
'reject' => __('Reject with an error message', 'contact-forms'),
);
}
/**
* The site-wide default spam action for a captcha type, from the settings page.
*
* reCAPTCHA v2 and v3 keep separate defaults because their checks fail for
* different reasons. A v3 failure is a low score computed invisibly, so
* absorbing it silently is the sensible default. A v2 failure most often means
* the visitor did not solve the checkbox they were shown - or took longer than
* the two minutes a token stays valid, or retried with a token Google had
* already consumed - and Google's guidance for that case is to surface the
* error and let them solve it again (the AJAX handler resets the widget, so a
* retry always carries a fresh token). Accepting those silently would file a
* real visitor's message as spam and send no notification, so v2 defaults to
* 'reject'.
*
* @since 2.3.0 The $type parameter, and the separate v2 default.
* @param string $type Field type: 'captcha' (reCAPTCHA v2) or 'captcha_v3'.
* @return string 'spam', 'trash', 'delete' or 'reject'.
*/
function accua_forms_captcha_default_spam_action($type = 'captcha_v3') {
$captcha_data = get_option('accua_forms_default_captcha_field_data', array());
$is_v2 = ('captcha' === $type);
$option_key = $is_v2 ? 'captcha_spam_action_v2' : 'captcha_spam_action';
$fallback = $is_v2 ? 'reject' : 'spam';
$action = isset($captcha_data[$option_key]) ? $captcha_data[$option_key] : '';
return isset(accua_forms_captcha_spam_action_options()[$action]) ? $action : $fallback;
}
/**
* Resolve the spam action configured on a captcha (v2) or captcha_v3 field
* instance: instance override, then the site-wide default for its type.
*
* @param array $istance_data The field instance data from the saved form.
* @param string $type Field type: 'captcha' (v2) or 'captcha_v3'.
* Defaults to the v3 resolution for backward
* compatibility with pre-2.3.0 callers.
* @return string 'spam', 'trash', 'delete' or 'reject'.
*/
function accua_forms_captcha_spam_action($istance_data, $type = 'captcha_v3') {
$action = isset($istance_data['spam_action']) ? $istance_data['spam_action'] : '';
return isset(accua_forms_captcha_spam_action_options()[$action]) ? $action : accua_forms_captcha_default_spam_action($type);
}
/**
* Find the spam action of the first captcha (v2) or captcha_v3 field of a
* form, if any.
*
* Note: when a submission was actually flagged, the handler follows the action
* recorded by the validator that failed (AccuaForm_Validation_CaptchaSpam), not
* this helper - a form can carry both a v2 and a v3 field with different
* actions. This remains for callers that need a form-level answer up front.
*
* @param array $form_data The saved form data (with 'fields').
* @return string The configured spam action, or the site-wide default if the
* form has no captcha field or no explicit setting.
*/
function accua_forms_captcha_form_spam_action($form_data) {
if (!empty($form_data['fields']) && is_array($form_data['fields'])) {
$avail_fields = get_option('accua_forms_avail_fields', array());
foreach ($form_data['fields'] as $istance_data) {
if (isset($istance_data['ref'], $avail_fields[$istance_data['ref']]['type'])
&& in_array($avail_fields[$istance_data['ref']]['type'], array('captcha', 'captcha_v3'), true)) {
return accua_forms_captcha_spam_action($istance_data, $avail_fields[$istance_data['ref']]['type']);
}
}
}
return accua_forms_captcha_default_spam_action();
}
/**
* The site-wide default for the captcha "Hide field title" option.
*
* @return bool True when captcha titles are hidden by default (ships true).
*/
function accua_forms_captcha_default_hide_title() {
$captcha_data = get_option('accua_forms_default_captcha_field_data', array());
if (!isset($captcha_data['captcha_hide_title'])) {
return true;
}
return !empty($captcha_data['captcha_hide_title']);
}
/**
* Whether the title of a captcha field instance (captcha, captcha_v3 or cap)
* is hidden on the rendered form. Instance override, then the site-wide
* default (hidden unless the administrator changed it). The label stays in
* the markup visually hidden (screen-reader only), so error summary links and
* assistive tech keep working.
*
* @param array $istance_data The field instance data from the saved form.
* @return bool
*/
function accua_forms_captcha_hide_title($istance_data) {
if (!isset($istance_data['hide_title'])) {
return accua_forms_captcha_default_hide_title();
}
return !empty($istance_data['hide_title']);
}
/**
* Normalize a reCAPTCHA v3 score threshold to the 0.0 - 1.0 range Google
* uses, rounded to two decimals.
*
* @param mixed $value The raw value (string from a form field, float, ...).
* @param float $fallback Returned when $value is not numeric.
* @return float
*/
function accua_forms_recaptcha3_clamp_score($value, $fallback = 0.5) {
if (!is_numeric($value)) {
return (float) $fallback;
}
$score = round((float) $value, 2);
if ($score < 0) {
return 0.0;
}
if ($score > 1) {
return 1.0;
}
return $score;
}
/**
* The site-wide default minimum reCAPTCHA v3 score, from the settings page.
*
* @return float 0.0 - 1.0 (ships 0.5, the value Google suggests).
*/
function accua_forms_recaptcha3_default_score_threshold() {
$captcha_data = get_option('accua_forms_default_captcha_field_data', array());
$stored = isset($captcha_data['recaptcha_v3_score_threshold']) ? $captcha_data['recaptcha_v3_score_threshold'] : null;
return accua_forms_recaptcha3_clamp_score($stored, 0.5);
}
/**
* Resolve the minimum reCAPTCHA v3 score a submission must reach on a given
* field instance: instance override, then the site-wide default.
*
* The accua_forms_recaptcha3_score_threshold filter still runs last, inside
* the validator, and receives this value as its default.
*
* @param array $istance_data The field instance data from the saved form.
* @return float 0.0 - 1.0.
*/
function accua_forms_recaptcha3_score_threshold($istance_data) {
$default = accua_forms_recaptcha3_default_score_threshold();
if (isset($istance_data['score_threshold']) && is_numeric($istance_data['score_threshold'])) {
return accua_forms_recaptcha3_clamp_score($istance_data['score_threshold'], $default);
}
return $default;
}
/**
* Placement options for the floating reCAPTCHA v3 badge.
*
* Google allows hiding the badge only if the reCAPTCHA branding is shown
* elsewhere in the user flow - with 'hidden' the field prints the required
* notice text under the form itself.
*
* @return array badge key => translated label.
*/
function accua_forms_recaptcha3_badge_options() {
return array(
'bottomright' => __('Bottom right', 'contact-forms'),
'bottomleft' => __('Bottom left', 'contact-forms'),
'hidden' => __('Hidden (the required reCAPTCHA notice is shown in the form instead)', 'contact-forms'),
);
}
/**
* The configured reCAPTCHA v3 badge placement.
*
* Site-wide, not per field: the badge is a single floating element shared by
* every reCAPTCHA on the page.
*
* @return string 'bottomright' (default), 'bottomleft' or 'hidden'.
*/
function accua_forms_recaptcha3_badge() {
$captcha_data = get_option('accua_forms_default_captcha_field_data', array());
$badge = isset($captcha_data['recaptcha_v3_badge']) ? $captcha_data['recaptcha_v3_badge'] : '';
return isset(accua_forms_recaptcha3_badge_options()[$badge]) ? $badge : 'bottomright';
}
/** @deprecated 2.2.40 Use accua_forms_captcha_spam_action_options(). */
function accua_forms_recaptcha3_spam_action_options() {
return accua_forms_captcha_spam_action_options();
}
/** @deprecated 2.2.40 Use accua_forms_captcha_spam_action(). */
function accua_forms_recaptcha3_spam_action($istance_data) {
return accua_forms_captcha_spam_action($istance_data);
}
/** @deprecated 2.2.40 Use accua_forms_captcha_form_spam_action(). */
function accua_forms_recaptcha3_form_spam_action($form_data) {
return accua_forms_captcha_form_spam_action($form_data);
}
function accua_forms_anonymize_ip($ip) {
$ip = (string) $ip;
$anonymize_ip_data = get_option('accua_forms_anonymize_ip_data',array());
if (empty($anonymize_ip_data['anonymize_ip_bytes'])) {
return $ip;
}
switch ($anonymize_ip_data['anonymize_ip_bytes']) {
case 1:
$ip = preg_replace('/\.[^.]+$/', '.xxx', $ip);
break;
case 2:
$ip = preg_replace('/\.[^.]+\.[^.]+$/', '.xxx.xxx', $ip);
break;
case 3:
$ip = preg_replace('/\.[^.]+\.[^.]+\.[^.]+$/', '.xxx.xxx.xxx', $ip);
break;
case 4:
$ip = '';
}
return $ip;
}
add_action('accua_form_submit', 'accua_forms_form_submission_handler', -10, 3);
function accua_forms_form_submission_handler($submittedID, $submittedData, $form) {
if (empty($GLOBALS['wp_rewrite'])) {
$GLOBALS['wp_rewrite'] = new WP_Rewrite();
}
if (!class_exists('AccuaConditionalReplacer')){
require_once('AccuaConditionalReplacer.php');
}
if (substr($submittedID, 0, 14) == '__accua-form__') {
$fid = substr($submittedID,14);
$form_data = _accua_forms_get_form_data($fid, false);
if ($form_data) {
global $wpdb;
$time = time();
$captcha_flag_key = 'accuaform_' . preg_replace('/[^A-Za-z0-9_]/', '_', $fid);
$stats_data = array(
'user_agent' => $form->stats['user_agent'],
'platform' => $form->stats['platform'],
'tentatives' => $form->stats['tentatives'],
'submit_method' => $form->stats['submit_method'],
);
// reCAPTCHA v3 returned a score for this submission: keep it so the
// administrator can see what the form actually scores and tune the
// threshold accordingly. Recorded whether the check passed or not.
if (class_exists('AccuaForm_Validation_Captcha3', false)) {
$recaptcha3_score = AccuaForm_Validation_Captcha3::getScore($captcha_flag_key);
if ($recaptcha3_score !== null) {
$stats_data['recaptcha3_score'] = $recaptcha3_score;
}
}
// Auto mark as spam: an email field carrying an address from the
// site-wide blocklist (settings page, Other settings tab). Recorded in
// the stats so the single submission page can say why it was classified;
// only the reason, never the address itself - afs_stats survives every
// read of the submission and is cleared on anonymization, so nothing
// personal belongs in it.
$has_blocklisted_email = accua_forms_submission_has_blocklisted_email($submittedData, $form_data);
if ($has_blocklisted_email) {
$stats_data['spam_reason'] = 'email_blocklist';
}
$afs_stats = _accua_forms_json_encode($stats_data);
$anonymized_ip = accua_forms_anonymize_ip($form->stats['ip']);
// Silent captcha classification: when the captcha (v2) or captcha_v3
// field of this form is configured with a silent spam action, a failed
// check passed validation but flagged the submission (see
// Validation/CaptchaSpam.php). The action comes from the validator that
// actually failed - since 2.3.0 v2 and v3 have different defaults, so a
// form carrying both must not follow whichever field happens to come
// first. A 'reject' action never flags, so a flagged key is always one
// of the three silent actions.
$captcha_spam_action = class_exists('AccuaForm_Validation_CaptchaSpam', false)
? AccuaForm_Validation_CaptchaSpam::getSpamAction($captcha_flag_key)
: '';
$captcha_flagged = in_array($captcha_spam_action, array('spam', 'trash', 'delete'), true);
$spam_action = $captcha_spam_action;
$is_spam = $captcha_flagged;
// The blocklist marks the submission as Spam whatever the captcha
// decided: a captcha judges how the form was filled in, not who filled
// it in. It never overrides a captcha classification that is already
// stricter (trash, delete) - only a submission the captcha let through
// is reclassified here.
if ($has_blocklisted_email && !$is_spam) {
$spam_action = 'spam';
$is_spam = true;
}
$insert_data = array (
'afs_form_id' => (string) $fid,
'afs_post_id' => (string) $form->stats['pid'],
'afs_ip' => $anonymized_ip,
'afs_uri' => (string) $form->stats['uri'],
'afs_referrer' => (string) $form->stats['referrer'],
'afs_lang' => (string) $form->stats['lang'],
'afs_created' => (string) gmdate('Y-m-d H:i:s', $form->stats['created']),
'afs_submitted' => (string) gmdate('Y-m-d H:i:s', $time),
'afs_stats' => (string) $afs_stats,
);
if ($is_spam) {
if ($spam_action === 'trash') {
$insert_data['afs_status'] = -1;
} elseif ($spam_action === 'spam') {
$insert_data['afs_lead_status'] = -1;
}
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Form submission insert requires direct query
$insert_ret = $wpdb->insert(
$wpdb->prefix . 'accua_forms_submissions',
$insert_data
);
if ($insert_ret) {
$submission_id = $form->stats['submission_id'] = $wpdb->insert_id;
} else {
$submission_id = $form->stats['submission_id'] = 0;
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Legitimate error logging for failed DB insert
error_log("[Contact Forms] unable to save submitted form data");
}
$review_submission_url = admin_url('admin.php').'?page=accua_forms_submissions_list&sid='.$submission_id;
$replace_map = array(
'__fid' => $fid,
'__subid' => $submission_id,
'__pid' => $form->stats['pid'],
'__ip' => $form->stats['ip'],
'__anonymized_ip' => $anonymized_ip,
'__uri' => $form->stats['uri'],
'__url' => $form->stats['url'],
'__referrer' => $form->stats['referrer'],
'__lang' => $form->stats['lang'],
'__locale' => $form->stats['locale'],
'__created' => $form->stats['created'],
'__created_day' => wp_date('l j F Y', $form->stats['created']),
'__created_day_month_year' => wp_date('j F Y', $form->stats['created']),
'__created_hour' => wp_date('G:i', $form->stats['created']),
'__submitted' => $time,
'__submitted_day' => wp_date('l j F Y', $time),
'__submitted_day_month_year' => wp_date('j F Y', $time),
'__submitted_hour' => wp_date('G:i', $time),
'__confirmation_emails_message' => $form_data['confirmation_emails_message'],
'__user_agent' => $form->stats['user_agent'],
'__platform' => $form->stats['platform'],
'__tentatives' => $form->stats['tentatives'],
'__submit_method' => $form->stats['submit_method'],
'__review_submission_url' => $review_submission_url,
);
$avail_fields = get_option('accua_forms_avail_fields', array());
$replace_map['__autoreply_email_raw'] = array();
$replace_map['__submitted_txt_raw'] = array();
$replace_map['__submitted_html_raw'] = array();
$replace_map['__submitted_json_raw'] = array();
$_field_data = array();
$_istance_data = array();
$field_data = array();
foreach ($submittedData as $istance_id => $value) {
if (empty($form_data['fields'][$istance_id])) {
continue;
}
$istance_data = $form_data['fields'][$istance_id];
if (empty($avail_fields[$istance_data['ref']])) {
$fieldset_data = array();
if (!empty($istance_data['ref'])) {
if ($istance_data['ref'] == '__fieldset-begin') {
$field_data = array(
'id' => '__fieldset-begin',
'name' => __('Fieldset begin', 'contact-forms'),
'type' => 'fieldset-begin',
'description' => '',
);
} elseif ($istance_data['ref'] == '__fieldset-end') {
$field_data = array(
'id' => '__fieldset-end',
'name' => __('Fieldset end', 'contact-forms'),
'type' => 'fieldset-end',
'description' => '',
);
}
}
} else {
$field_data = $avail_fields[$istance_data['ref']];
}
$field_data += array(
'id' => '__html',
'name' => __( 'Custom HTML content', 'contact-forms'),
'type' => 'html',
'description' => __( 'Use this special field to inject raw HTML in the form. You can use this multiple times.', 'contact-forms'),
'default_value' => '',
'allowed_values' => '',
);
/*
echo "\n";
*/
$istance_data += array(
'istance_id' => $field_data['id'],
'widget_number' => '',
'ref' => $field_data['id'],
'label' => $field_data['name'],
'default_value' => $field_data['default_value'],
'allowed_values' => $field_data['allowed_values'],
);
$type = $field_data['type'];
$file_download_url = '';
switch ($field_data['type']) {
case 'checkbox':
case 'submit':
if (empty($value)) {
$replace_map[$istance_data['istance_id']] = $value = '';
} else {
if (empty($istance_data['default_value'])) {
$replace_map[$istance_data['istance_id']] = 'Checked';
$value = '1';
} else {
$replace_map[$istance_data['istance_id']] = $value = $istance_data['default_value'];
}
}
break;
case 'multiselect':
case 'multicheckbox':
case 'select':
case 'radio':
$el = $form->getElementByName($istance_id);
$opts = $el->getOptions();
if (!is_array($value)) {
$value = array($value);
}
$label = array();
foreach ($value as $val) {
if (isset($opts[$val])) {
$label[] = $opts[$val];
}
}
$replace_map['__label_'.$istance_data['istance_id']] = implode(", ", $label);
$replace_map[$istance_data['istance_id']] = implode(', ',$value);
$value = implode('|',$value);
break;
case 'post-multicheckbox':
if (is_array($value) && $value) {
$el = $form->getElementByName($istance_id);
$opts = $el->getOptions();
$value2 = array();
$titles = array();
$ids = array();
$urls = array();
foreach ($value as $val) {
if (isset($opts[$val])) {
$titles[] = $opts[$val];
$ids[] = $val;
$urls[] = get_permalink($val);
$value2[] = $val . ': ' . trim(preg_replace('/[\s\n\r]+/', ' ', $opts[$val]));
}
}
$replace_map['__label_'.$istance_data['istance_id']] = $replace_map['__post_title_'.$istance_data['istance_id']] = implode("\n", $titles);
$replace_map['__post_id_'.$istance_data['istance_id']] = implode("\n", $ids);
$replace_map['__post_url_'.$istance_data['istance_id']] = implode("\n", $urls);
$replace_map[$istance_data['istance_id']] = $value = implode("\n", $value2);
} else {
$replace_map['__label_'.$istance_data['istance_id']] = $replace_map['__post_title_'.$istance_data['istance_id']] = '';
$replace_map['__post_id_'.$istance_data['istance_id']] = '';
$replace_map['__post_url_'.$istance_data['istance_id']] = '';
$replace_map[$istance_data['istance_id']] = $value = '';
}
break;
case 'post-select':
if ($value !== '') {
$post = get_post(absint($value));
// The submitted ID must belong to the post type the field is
// configured for and have a status the field may expose (publish,
// plus private when explicitly configured); otherwise any post ID
// would be accepted.
$expected_post_type = isset($istance_data['post_type']) ? $istance_data['post_type'] : 'page';
$ps_allowed_statuses = array('publish');
$ps_element = $form->getElementByName($istance_id);
if ($ps_element instanceof AccuaForm_Element_PostSelect) {
$expected_post_type = $ps_element->getEffectivePostType();
$ps_allowed_statuses = $ps_element->getAllowedPostStatuses();
}
if ($post && in_array($post->post_status, $ps_allowed_statuses, true) && $post->post_type === $expected_post_type) {
$title = $post->post_title;
$replace_map['__label_'.$istance_data['istance_id']] = $replace_map['__post_title_'.$istance_data['istance_id']] = $title;
$replace_map['__post_id_'.$istance_data['istance_id']] = $value;
$replace_map['__post_url_'.$istance_data['istance_id']] = get_permalink($value);
$value = $value . ': ' . trim(preg_replace('/[\s\n\r]+/', ' ', $title));
} else {
$replace_map['__label_'.$istance_data['istance_id']] = $replace_map['__post_title_'.$istance_data['istance_id']] = '';
$replace_map['__post_id_'.$istance_data['istance_id']] = '';
$replace_map['__post_url_'.$istance_data['istance_id']] = '';
$value = '';
}
}
$replace_map[$istance_data['istance_id']] = $value;
break;
case 'autoreply_email':
if ($value !== '') {
$replace_map['__autoreply_email_raw'][] = $value;
}
$replace_map[$istance_data['istance_id']] = $value;
break;
case 'file':
//TODO: move temp file to "{$submission_id}_{$field_data['id']}_{$file['name']}"; value is $file['name']
$buildid = $submittedData['_AccuaForm_buildID'];
$file = $form->getFile($istance_id);
if ($value !== null && $value !== '' && $file) {
if ($form->renameFile($istance_id, "{$submission_id}_{$field_data['id']}_{$file['name']}")) {
$urlfield = rawurlencode($istance_data['istance_id']);
$urlfile = rawurlencode($value);
$token = accua_forms_generate_download_token($submission_id);
$file_download_url = admin_url('admin-ajax.php') . "?action=accua_forms_download_submitted_file&subid={$submission_id}&field={$urlfield}&file={$urlfile}&nonce=" . wp_create_nonce('accua_forms_download_nonce')."&token={$token}&_wpnonce=" . wp_create_nonce('download_file_' . $submission_id . '_' . $urlfield);
}
}
$replace_map[$istance_data['istance_id']] = $value;
$replace_map['__download_'.$istance_data['istance_id']] = $file_download_url;
break;
case 'password':
case 'password-and-confirm':
$value = trim($value);
$replace_map[$istance_data['istance_id']] = $value;
$type = 'hashed-password';
if ($value !== '') {
$value = wp_hash_password($value);
}
$replace_map['__hashed_'.$istance_data['istance_id']] = $value;
break;
default:
$replace_map[$istance_data['istance_id']] = $value;
}
switch ($field_data['type']) {
case 'fieldset-begin':
$fieldset_label = !empty($istance_data['label']) ? esc_html($istance_data['label']) : esc_html($istance_data['istance_id']);
$replace_map['__submitted_txt_raw'][$istance_data['istance_id']] = "\n--- {$istance_data['label']} ---";
$replace_map['__submitted_json_raw'][$istance_data['istance_id']] = $value;
$replace_map['__submitted_html_raw'][$istance_data['istance_id']] = "{$fieldset_label}
";
break;
case 'fieldset-end':
break;
case 'file':
$replace_map['__submitted_txt_raw'][$istance_data['istance_id']] = "{$istance_data['istance_id']}\t$value\t$file_download_url";
$replace_map['__submitted_json_raw'][$istance_data['istance_id']] = "$value\t$file_download_url";
$replace_map['__submitted_html_raw'][$istance_data['istance_id']] = "{$istance_data['istance_id']}
".esc_html($value)."";
break;
case 'email':
case 'autoreply_email':
$replace_map['__submitted_txt_raw'][$istance_data['istance_id']] = "{$istance_data['istance_id']}\t$value";
$replace_map['__submitted_json_raw'][$istance_data['istance_id']] = $value;
$replace_map['__submitted_html_raw'][$istance_data['istance_id']] = "{$istance_data['istance_id']}
$value_html";
break;
case 'password':
case 'password-and-confirm':
break;
default:
$replace_map['__submitted_txt_raw'][$istance_data['istance_id']] = "{$istance_data['istance_id']}\t$value";
$replace_map['__submitted_json_raw'][$istance_data['istance_id']] = $value;
$replace_map['__submitted_html_raw'][$istance_data['istance_id']] = "{$istance_data['istance_id']}
".esc_html($value);
} if ($submission_id) {
// Ensure value is never NULL to prevent database errors
$safe_value = $value === null ? '' : $value;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Form field values insert requires direct query
$wpdb->insert(
$wpdb->prefix . 'accua_forms_submissions_values',
array (
'afsv_sub_id' => $submission_id,
'afsv_field_id' => $istance_data['istance_id'],
'afsv_type' => $type,
'afsv_value' => $safe_value,
),
array('%d','%s','%s','%s')
);
}
$_field_data[$istance_data['istance_id']] = $field_data;
$_istance_data[$istance_data['istance_id']] = $istance_data;
}
$replace_map['__autoreply'] = (bool) ($replace_map['__autoreply_email_raw']
&& $form_data['confirmation_emails_subject']
&& $form_data['confirmation_emails_message']);
accua_forms_aggregate_submitted_data($replace_map);
//Older undocumented filter, maintained for backward compatibility. In fact filter accua_forms_form_submission_handler is called before accua_forms_submission, but for the rest they are the same
$replace_map = apply_filters('accua_forms_form_submission_handler', $replace_map, $fid, $submittedData, $form, $_field_data, $_istance_data);
//Newer filter, with an easier name
$replace_map = apply_filters('accua_forms_submission', $replace_map, $fid, $submittedData, $form, $_field_data, $_istance_data);
$submitted_html = "
\n
\n
" . $replace_map['__submitted_html'] . "
";
$submitted_html = str_replace("class='valori_submitted'", "class='valori_submitted' style='vertical-align:top;padding:4px 0;overflow-wrap:break-word;word-break:break-word;'", $submitted_html);
$confirmation_emails_message = $replace_map['__confirmation_emails_message'];
unset($replace_map['__submitted_html'], $replace_map['__confirmation_emails_message'], $replace_map['__submitted_txt_raw'], $replace_map['__submitted_html_raw'], $replace_map['__submitted_json_raw'], $replace_map['__autoreply_email_raw']);
$replace_map_html = array();
foreach($replace_map as $key => $value) {
$replace_map_html["!$key"] = wp_kses($value, 'post');
$replace_map_html[$key] = esc_attr($value);
}
$replace_map['__submitted_html'] = $replace_map_html['__submitted_html'] = $replace_map_html['!__submitted_html'] = $submitted_html;
$replacer_html = new AccuaConditionalReplacer($replace_map_html);
$confirmation_emails_message = $replace_map['__confirmation_emails_message'] = $replacer_html->doReplace($confirmation_emails_message);
$replacer_html->appendPattern(array('__confirmation_emails_message' => $confirmation_emails_message, '!__confirmation_emails_message' => $confirmation_emails_message));
$replacer = new AccuaConditionalReplacer($replace_map);
$form_data_replaced = array();
$settings = array(
'emails_from_name',
'emails_from',
'admin_emails_to',
'emails_bcc',
'admin_emails_subject',
'confirmation_emails_subject',
);
foreach($settings as $i) {
$form_data_replaced[$i] = $replacer->doReplace($form_data[$i]);
}
$settings_html = array(
'success_message',
'error_message',
'admin_emails_message',
);
foreach($settings_html as $i) {
$form_data_replaced[$i] = $replacer_html->doReplace($form_data[$i]);
}
// Track mail sending success for showing appropriate message
$mail_success = true;
$mail1 = true;
$mail2 = true;
$header = array("Content-Type: text/html; charset=".get_option('blog_charset'));
$emails_from = trim($form_data_replaced['emails_from']);
if (strpos($emails_from, '@') !== false) {
if ((strpos($emails_from, '<') === false) && is_email($emails_from)) {
$emails_from_name = trim($form_data_replaced['emails_from_name']);
if ($emails_from_name !== '') {
$emails_from = "=?" . get_bloginfo('charset') . "?B?" . base64_encode($emails_from_name) . "?= <$emails_from>";
}
}
$header[] = 'From: '.$emails_from;
}
if ($form_data_replaced['emails_bcc']) {
$header[] = 'Bcc: '.$form_data_replaced['emails_bcc'];
}
if (!$is_spam
&& $form_data_replaced['admin_emails_to']
&& $form_data_replaced['admin_emails_subject']) {
/*
$admin_tos = explode(',', strtr($form_data_replaced['admin_emails_to'], "\n\t\r;", ',,,,'));
foreach ($admin_tos as $admin_to) {
$mail1 = wp_mail(trim($admin_to), $form_data_replaced['admin_emails_subject'], $form_data_replaced['admin_emails_message'], $header);
}
*/
$mail1 = wp_mail($form_data_replaced['admin_emails_to'], $form_data_replaced['admin_emails_subject'],''.wpautop($form_data_replaced['admin_emails_message']).'', $header);
if (!$mail1) {
$mail_success = false;
}
}
if (!$is_spam
&& $replace_map['__autoreply'] && $replace_map['__autoreply_email']
&& $form_data_replaced['confirmation_emails_subject']
&& $confirmation_emails_message) {
$mail2 = wp_mail($replace_map['__autoreply_email'], $form_data_replaced['confirmation_emails_subject'], ''.wpautop($confirmation_emails_message).'', $header);
if (!$mail2) {
$mail_success = false;
}
}
// Determine which message to show based on mail success and user settings
if ($mail_success) {
// Show success message unless "Don't show any messages" is selected
if (empty($form_data['success_message_no_message'])) {
$message_content = trim($form_data_replaced['success_message']);
if ($message_content !== '') {
AccuaForm::appendSubmittedMessages(wpautop($message_content));
}
}
} else {
// Mail failed - show error message unless "Don't show any messages" is selected
if (empty($form_data['error_message_no_message'])) {
$error_content = trim($form_data_replaced['error_message']);
if ($error_content !== '') {
AccuaForm::appendSubmittedMessages(wpautop($error_content));
}
}
}
if ($is_spam) {
if ($spam_action === 'delete' && $submission_id) {
// Remove the stored rows and any uploaded files; the visitor still
// saw the normal success message.
accua_forms_erase_submission($submission_id, 'delete');
$submission_id = $form->stats['submission_id'] = 0;
}
// The two captcha hooks keep firing only for captcha classifications:
// they are documented as such, and a listener acting on them (feeding
// a captcha's own reporting API, tuning a score threshold) would be
// misled by a submission no captcha ever looked at. The blocklist has
// the type-agnostic accua_forms_spam_submission below instead.
if ($captcha_flagged) {
/**
* Fires when a submission was silently classified as spam by a
* captcha field (reCAPTCHA v2 or v3).
*
* @param int $submission_id The stored submission id (0 when the spam action is 'delete').
* @param string $spam_action The configured action: 'spam', 'trash' or 'delete'.
* @param string $fid The form id.
*/
do_action('accua_forms_captcha_spam_submission', $submission_id, $captcha_spam_action, $fid);
/**
* Deprecated alias of accua_forms_captcha_spam_submission, kept for
* backward compatibility. Since 2.2.40 it also fires for reCAPTCHA v2
* classifications, not only v3.
*
* @param int $submission_id The stored submission id (0 when the spam action is 'delete').
* @param string $spam_action The configured action: 'spam', 'trash' or 'delete'.
* @param string $fid The form id.
*/
do_action('accua_forms_recaptcha3_spam_submission', $submission_id, $captcha_spam_action, $fid);
}
/**
* Fires when a submission was silently classified as spam by any of
* the plugin's spam checks.
*
* @since 2.3.1
* @param int $submission_id The stored submission id (0 when the spam action is 'delete').
* @param string $spam_action The action applied: 'spam', 'trash' or 'delete'.
* @param string $fid The form id.
* @param string $spam_reason What classified it: 'captcha' or 'email_blocklist'.
*/
do_action(
'accua_forms_spam_submission',
$submission_id,
$spam_action,
$fid,
$captcha_flagged ? 'captcha' : 'email_blocklist'
);
}
/*
echo "";
*/
}
}
}
function accua_forms_get_submission_data($subid, $options = array()){
//TODO: Completa e usa per sostituire i campi
global $wpdb;
$subid = (int) $subid;
$options += array(
'extra' => true,
'file_format' => 'name',
);
$ret = array();
if ($options['extra']) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Submission data lookup requires direct query
$query1 = $wpdb->prepare(
"SELECT *
FROM `{$wpdb->prefix}accua_forms_submissions`
WHERE afs_id = %d",
$subid
);
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- $query1 is prepared above, submission lookup requires direct query
$data = $wpdb->get_row($query1);
if (!empty($data)) {
$created = $data->afs_created;
$created[10] = 'T';
$created.='.00+00:00';
$created = strtotime($created);
$submitted = $data->afs_submitted;
$submitted[10] = 'T';
$submitted.='.00+00:00';
$submitted = strtotime($submitted);
if ($data->afs_stats) {
$stats = json_decode($data->afs_stats, true);
if (!$stats) {
$stats = array();
}
} else {
$stats = array();
}
$stats += array(
'user_agent' => '',
'platform' => '',
'tentatives' => '',
'submit_method' => '',
// Only present on submissions verified by a reCAPTCHA v3 field.
'recaptcha3_score' => '',
);
$ret += array(
'__fid' => $data->afs_form_id,
'__subid' => $subid,
'__pid' => $data->afs_post_id,
'__ip' => $data->afs_ip,
'__anonymized_ip' => accua_forms_anonymize_ip($data->afs_ip),
'__uri' => $data->afs_uri,
'__referrer' => $data->afs_referrer,
'__lang' => $data->afs_lang,
'__created' => $created,
'__created_day' => wp_date('l j F Y', $created),
'__created_hour' => wp_date('G:i', $created),
'__submitted' => $submitted,
'__submitted_day' => wp_date('l j F Y', $submitted),
'__submitted_hour' => wp_date('G:i', $submitted),
'__user_agent' => $stats['user_agent'],
'__platform' => $stats['platform'],
'__tentatives' => $stats['tentatives'],
'__submit_method' => $stats['submit_method'],
'__recaptcha3_score' => $stats['recaptcha3_score'],
);
}
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Submission values lookup requires direct query
$query2 = $wpdb->prepare(
"SELECT *
FROM `{$wpdb->prefix}accua_forms_submissions_values`
WHERE afsv_sub_id = %d",
$subid
);
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- $query2 is prepared above, submission values lookup requires direct query
$data2 = $wpdb->get_results($query2, OBJECT);
foreach ($data2 as $row) {
switch ($row->afsv_type) {
case 'file' :
if ($options['file_format'] == 'url' || $options['file_format'] == 'link') {
$fieldid = rawurlencode($row->afsv_field_id);
$filename = rawurlencode($row->afsv_value);
$url = admin_url('admin-ajax.php') . "?action=accua_forms_download_submitted_file&subid={$row->afsv_sub_id}&field={$fieldid}&file={$filename}&nonce=" . wp_create_nonce('accua_forms_download_nonce') . "&_wpnonce=" . wp_create_nonce('download_file_' . $row->afsv_sub_id . '_' . $fieldid);
if(isset($options['token'])){
$url .= '&token='.$options['token'];
}
if ($options['file_format'] == 'link'){
$url = esc_url($url);
$filename = esc_html($row->afsv_value);
$fielddata = "{$filename}";
} else {
$fielddata = $url;
}
} else { // $options['file_format'] == 'name'
$fielddata = $row->afsv_value;
}
break;
default:
$fielddata = $row->afsv_value;
}
$ret[$row->afsv_field_id] = $fielddata;
}
return $ret;
}
add_shortcode( 'accua-form', 'accua_forms_shortcode_handler' );
function accua_forms_shortcode_handler($atts, $content = '', $code = '') {
if (empty($atts['fid'])) {
return '';
}
$fid = $atts['fid'];
$form_data = _accua_forms_get_form_data($fid, false);
if (! $form_data) {
// In preview mode, allow unsaved (new) forms to render using draft + defaults
if (! apply_filters('accua_forms_use_draft_for_preview', false)) {
return '';
}
$default_form_data = get_option('accua_forms_default_form_data', array());
$empty_form_data = _accua_forms_get_form_data(false);
$form_data = array('_overrided' => array()) + $default_form_data + $empty_form_data;
}
// Note: Preview field order override is handled in accua_forms_form_generate()
// which applies the filter there for live preview
$fid = '__accua-form__'.$fid;
$out = '';
if (AccuaForm::getSubmittedID() == $fid) {
/* return "
Form submitted.\n\nData: " . print_r(AccuaForm::getSubmittedData(), true) . '
'; */
// Get per-form messages (supports multiple forms on same page)
$messages = AccuaForm::getSubmittedMessages($fid);
if ($messages && trim($messages) !== '') {
$out .= '
'.$messages.'
';
}
// Non-AJAX fallback: set URL hash and scroll to result messages on page load.
// The anchor elements are only created by the AJAX JS block (which is not output for
// non-AJAX forms), so we scroll to the messages div by class instead.
$anchor_suffix = preg_replace('/[^a-zA-Z0-9]+/', '_', str_replace('__accua-form__', '', $fid));
$hash_type = AccuaForm::isValid() ? 'formSubmitSuccess' : 'formSubmitInvalid';
$anchor_full = esc_js($hash_type . '-' . $anchor_suffix);
$out .= '';
if (AccuaForm::isValid()) {
return $out;
}
$form = AccuaForm::getSubmittedForm();
} else {
$analytics_data = get_option('accua_forms_default_analytics_data',array());
// Check for preview layout override (allows live preview of layout changes before save)
$layout = $form_data['layout'];
$preview_layout_override = apply_filters('accua_forms_preview_layout_override', '');
if ($preview_layout_override) {
$layout = $preview_layout_override;
}
// If layout is empty (meaning "use default"), resolve to the global default layout
if (empty($layout)) {
$default_form_data = get_option('accua_forms_default_form_data', array());
$layout = !empty($default_form_data['layout']) ? $default_form_data['layout'] : 'sidebyside';
}
$params = array(
'layout' => $layout,
'title' => $form_data['title'],
'track_submit' => !empty($analytics_data['analytics_track_submit']),
'track_fields' => !empty($analytics_data['analytics_track_fields']),
'gads_conversion_tracking_code' => $form_data['gads_conversion_tracking_code'],
);
$form = AccuaForm::create($fid, $params);
}
$out .= $form->render(true);
$doing_ajax = function_exists('wp_doing_ajax') ? wp_doing_ajax() : (defined( 'DOING_AJAX' ) && DOING_AJAX);
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check for Yoast SEO compatibility, strips HTML for preview
if ($doing_ajax && isset($_REQUEST['action']) && ($_REQUEST['action'] === 'wpseo_filter_shortcodes')) {
$strip_regexp = '/(