# contact-forms/2.2.32/accua-forms.php

Contact Forms by Cimatti, version 2.2.32. 4,122 lines.

- Page: https://pluginprobe.com/plugins/contact-forms/2.2.32/code/accua-forms.php
- Raw: https://pluginprobe.com/plugins/contact-forms/2.2.32/raw/accua-forms.php
- Modified: 2026-07-13T10:05:26+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/contact-forms/2.2.32/code/accua-forms.php#L10-L20`.

```php
<?php
if ( ! defined( 'ABSPATH' ) ) exit;

require_once __DIR__ . '/admin/fields-page.php';
require_once __DIR__ . '/admin/settings-page.php';
require_once __DIR__ . '/admin/form-editor.php';
require_once __DIR__ . '/includes/privacy.php';

/**
 * Base fill colour for the admin sidebar menu icon.
 *
 * WordPress core (svg-painter.js) recolours base64 SVG menu icons to the active
 * admin colour scheme on load. We resolve that same base colour here and bake it
 * into the icon so the first server-rendered paint already matches the painted
 * result, avoiding a brief flash of a differently coloured icon before the JS
 * repaint. Colour schemes are registered on admin_init (priority 1) which runs
 * after admin_menu, so the exact colour is applied later by
 * accua_forms_paint_menu_icon() rather than at menu-registration time.
 */
function accua_forms_admin_menu_icon_color(){
  global $_wp_admin_css_colors;
  $scheme = get_user_option('admin_color');

  if ( empty($scheme) || ! isset($_wp_admin_css_colors[$scheme]) ) {
    $scheme = 'modern';
  }

  if ( ! empty($_wp_admin_css_colors[$scheme]->icon_colors['base']) ) {
    return $_wp_admin_css_colors[$scheme]->icon_colors['base'];
  }

  if ( ! empty($_wp_admin_css_colors['modern']->icon_colors['base']) ) {
    return $_wp_admin_css_colors['modern']->icon_colors['base'];
  }

  return '#a7aaad'; // WordPress default menu icon base colour.
}

/**
 * Monochrome sidebar menu icon as a base64 data URI.
 *
 * The standalone brand icon (assets/img/accua-contacts-forms.svg) stays coloured
 * and is used unchanged in page headers and other contexts; only the sidebar menu
 * icon is neutral, per the WordPress.org plugin guidelines.
 */
function accua_forms_admin_menu_icon(){
  $color = accua_forms_admin_menu_icon_color();
  $svg   = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44.46 44.46"><path fill="' . esc_attr($color) . '" d="m23.97,28.72c1.85,0,2.93-.11,2.93-.11,1.49-.15,2.75.95,2.8,2.44l.01.14c.05,1.5-1.11,2.96-2.58,3.25,0,0-2.53.5-5.25.5-7.24,0-10.11-3.84-10.11-12.98,0-8.6,3.01-12.44,10.26-12.44,2.82,0,5.11.47,5.11.47,1.47.3,2.63,1.78,2.57,3.27l-.01.15c-.05,1.5-1.31,2.6-2.8,2.45,0,0-1.13-.12-2.98-.12-4.67,0-5.54,1.6-5.54,6.22,0,5.2.92,6.76,5.59,6.76M33.36,1.93c-1.06-1.06-3.15-1.93-4.65-1.93H15.75c-1.5,0-3.59.87-4.65,1.93L1.93,11.1C.87,12.16,0,14.25,0,15.75v12.97c0,1.5.87,3.59,1.93,4.65l9.17,9.17c1.06,1.06,3.15,1.93,4.65,1.93h12.97c1.5,0,3.59-.87,4.65-1.93l9.17-9.17c1.06-1.06,1.93-3.15,1.93-4.65V15.75c0-1.5-.87-3.59-1.93-4.65z"/></svg>';
  return 'data:image/svg+xml;base64,' . base64_encode($svg);
}

/**
 * Repaint the sidebar menu icon with the active colour scheme's base colour.
 *
 * Runs on admin_init (priority 20, after register_admin_color_schemes at 1) when
 * the colour schemes are available. The icon set at menu-registration time uses
 * the fallback colour; here we overwrite it in the $menu global with the exact
 * scheme colour so the first paint matches svg-painter.js and there is no flash.
 */
add_action('admin_init', 'accua_forms_paint_menu_icon', 20);
function accua_forms_paint_menu_icon(){
  global $menu;
  if ( ! is_array($menu) ) {
    return;
  }
  foreach ( $menu as $i => $item ) {
    if ( isset($item[2]) && 'accua_forms' === $item[2] ) {
      $menu[$i][6] = accua_forms_admin_menu_icon();
      break;
    }
  }
}

add_action('admin_menu', 'accua_forms_menu', -95);
function accua_forms_menu(){
  $dashboard_admin_page=add_menu_page('Contact Forms by Cimatti', 'Contact Forms', 'manage_options', 'accua_forms', 'accua_forms_dashboard_page', accua_forms_admin_menu_icon(), '90.90300');
  add_action('load-'.$dashboard_admin_page, 'accua_forms_dashboard_page_head');

  add_submenu_page('accua_forms', 'Contact Forms by Cimatti', 'Dashboard', 'manage_options', "accua_forms", 'accua_forms_dashboard_page');

  $form_edit_page = add_submenu_page('accua_forms', 'Forms', 'Forms', 'manage_options', "accua_forms_list", 'accua_forms_list_page');
  add_action('admin_head-'.$form_edit_page, 'accua_forms_edit_page_head');
  add_action( 'admin_print_styles-'.$form_edit_page, 'accua_forms_edit_page_head_styles');
  add_action( 'admin_print_scripts-'.$form_edit_page, 'accua_forms_edit_page_head_scripts');

  $form_add_page = add_submenu_page('accua_forms', __('Add new form', 'contact-forms'), __('Add new', 'contact-forms'), 'manage_options', "accua_forms_add", 'accua_forms_add_page');
  add_action('admin_head-'.$form_add_page, 'accua_forms_edit_page_head');
  add_action( 'admin_print_styles-'.$form_add_page, 'accua_forms_edit_page_head_styles');
  add_action( 'admin_print_scripts-'.$form_add_page, 'accua_forms_edit_page_head_scripts');

  $form_submissions_page =  add_submenu_page('accua_forms', __('Forms submissions', 'contact-forms') , __('Submissions', 'contact-forms'), 'manage_options', "accua_forms_submissions_list", '__accua_forms_submissions_list_page');
  add_action('load-'.$form_submissions_page, 'accua_forms_submissions_list_page_load');
  add_action('admin_head-'.$form_submissions_page, 'accua_forms_submissions_list_page_head');
  add_action( 'admin_print_styles-'.$form_submissions_page, 'accua_forms_edit_page_head_styles');

  $form_fields_page = add_submenu_page('accua_forms', __( 'Form fields', 'contact-forms'), __('Fields', 'contact-forms'), 'manage_options', "accua_forms_fields", 'accua_forms_fields_page');
  add_action( 'admin_print_styles-'.$form_fields_page, 'accua_forms_edit_page_head_styles');
  add_action( 'admin_print_scripts-'.$form_fields_page, 'accua_forms_fields_page_enqueue_scripts');

  $settings_page = add_submenu_page('accua_forms', __( 'Default Forms settings', 'contact-forms'), __('Settings', 'contact-forms'), 'manage_options', "accua_forms_settings", 'accua_forms_settings_page');
  add_action( 'admin_print_styles-'.$settings_page, 'accua_forms_edit_page_head_styles');
  add_action( 'admin_print_scripts-'.$settings_page, 'accua_forms_settings_page_head_scripts');

  wp_enqueue_script('jquery-form');
  wp_enqueue_script('jquery-color');
  wp_enqueue_script('jquery-ui-core');
  wp_enqueue_script('jquery-ui-sortable');
  wp_enqueue_script('jquery-ui-draggable');
  wp_enqueue_script('jquery-ui-droppable');
  wp_enqueue_script('jquery-ui-selectable');
  wp_enqueue_script('jquery-ui-resizable');
  wp_enqueue_script('jquery-ui-dialog');
  wp_enqueue_style('wp-jquery-ui-dialog');
}

function accua_forms_report_page_head(){
  $column_list = array(
    'month' => __( 'Month', 'contact-forms'),
    'unique_submissions' => __( 'Unique submissions', 'contact-forms') ,
    'submissions' => __('Total submissions', 'contact-forms'),
  );
  global $hook_suffix;
  register_column_headers($hook_suffix, $column_list);
}


function accua_forms_report_page() {
?>
  <div id="accua_forms_report_page" class="accua_forms_admin_page wrap">
    <h2>Forms submissions report</h2>

<?php
  global $wpdb, $hook_suffix;
  $months = array(1 => 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');

  $query = "SELECT YEAR(sub_date) AS `year`, MONTH(sub_date) AS `month`, COUNT(DISTINCT `email`) AS `unique_submissions`, COUNT(*) AS `submissions`
    FROM `{$wpdb->prefix}cformssubmissions`
    GROUP BY `year`, `month`
    ORDER BY `year` DESC, `month` DESC";

  // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- No user input in query
  $results = $wpdb->get_results($query);

  if ($results) {
?>
  <style type="text/css">
  .column-submissions, .column-unique_submissions {
    text-align: right !important;
  }
  </style>
  <table class="widefat" id="stnl_review_reviewed">
    <thead>
      <tr><?php print_column_headers($hook_suffix); ?></tr>
    </thead>

    <tfoot>
      <tr><?php print_column_headers($hook_suffix, false); ?></tr>
    </tfoot>

    <tbody>
<?php
    $alternate = false;
    $hidden = get_hidden_columns($hook_suffix);
    foreach ($results as $result){
      $month = esc_html( $months[$result->month] );
      $year = esc_html( $result->year );
      $unique_submissions = esc_html( $result->unique_submissions );
      $submissions = esc_html( $result->submissions );
      $alternate_class = ( $alternate = ! $alternate ) ? 'alternate' : '';
      $month_style     = in_array( 'month', $hidden, true ) ? " style='display:none;'" : '';
      $unique_style    = in_array( 'unique_submissions', $hidden, true ) ? " style='display:none;'" : '';
      $sub_style       = in_array( 'submissions', $hidden, true ) ? " style='display:none;'" : '';
      // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All variables pre-escaped above
      echo "<tr class='iedit " . esc_attr( $alternate_class ) . "'>\n";
      // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $month, $year pre-escaped with esc_html()
      echo "<td class='column-month'" . $month_style . ">" . $month . " " . $year . "</td>\n";
      // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $unique_submissions pre-escaped with esc_html()
      echo "<td class='column-unique_submissions'" . $unique_style . ">" . $unique_submissions . "</td>\n";
      // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $submissions pre-escaped with esc_html()
      echo "<td class='column-submissions'" . $sub_style . ">" . $submissions . "</td>\n";
      echo "</tr>\n";
    }
?>
    </tbody>
  </table>
<?php
  }
?>

  </div>
<?php
}

function accua_forms_edit_page_head_styles() {
  wp_enqueue_style( 'accua-forms-admin', plugins_url('assets/css/admin.css', ACCUA_FORMS_FILE), array(), ACCUA_FORMS_CSS_VERSION); //
}

add_action( 'admin_enqueue_scripts', 'accua_forms_enqueue_deactivation_modal' );
function accua_forms_enqueue_deactivation_modal( $hook ) {
  if ( $hook !== 'plugins.php' ) {
    return;
  }

  wp_enqueue_script(
    'accua-forms-deactivation-modal',
    plugins_url( 'assets/js/admin/deactivation-modal.js', ACCUA_FORMS_FILE ),
    array( 'jquery' ),
    ACCUA_FORMS_JS_VERSION,
    true
  );

  wp_localize_script( 'accua-forms-deactivation-modal', 'accuaFormsDeactivation', array(
    'ajaxUrl'        => admin_url( 'admin-ajax.php' ),
    'nonce'          => wp_create_nonce( 'accua_forms_deactivation_cleanup' ),
    'pluginBasename' => plugin_basename( ACCUA_FORMS_FILE ),
    'i18n'           => array(
      'title'           => __( 'What would you like to do with your Contact Forms data?', 'contact-forms' ),
      'description'     => __( 'You are about to deactivate Contact Forms. Choose what to do with your existing data:', 'contact-forms' ),
      'deleteAll'       => __( 'Delete all data', 'contact-forms' ),
      'deleteAllDesc'   => __( 'Permanently remove all forms, submissions, settings, and uploaded files. This cannot be undone.', 'contact-forms' ),
      'anonymizeAll'    => __( 'Anonymize all submissions', 'contact-forms' ),
      'anonymizeAllDesc'=> __( 'Replace personal data with placeholders and set IPs to 0.0.0.0. Forms and settings will be kept. This cannot be undone.', 'contact-forms' ),
      'skip'            => __( 'Just deactivate', 'contact-forms' ),
      'skipDesc'        => __( 'Keep all data. You can reactivate the plugin later.', 'contact-forms' ),
      'confirmDelete'   => __( 'Are you sure? This will permanently delete ALL forms, submissions, settings, and uploaded files. This cannot be undone.', 'contact-forms' ),
      'confirmAnonymize'=> __( 'Are you sure? This will anonymize ALL submissions, replacing personal data with placeholders. This cannot be undone.', 'contact-forms' ),
      'processing'      => __( 'Processing…', 'contact-forms' ),
      'cancel'          => __( 'Cancel', 'contact-forms' ),
    ),
  ) );
}

function accua_forms_form_fields_order() {
  if (!current_user_can('manage_options')){
    wp_die( -1, 403 );
  }
  check_ajax_referer('edit_form', '_nonce_edit_form');

  $post = stripslashes_deep($_POST);

  if (empty($post['sidebars'])) {
    die('-1');
  }

  // Save to draft instead of directly to database
  foreach ($post['sidebars'] as $sidebar_id => $order) {
    if (strpos($sidebar_id, 'cimatti-accua-fields-form-area-') !== 0){
      die('-1');
    }
    $fid = substr($sidebar_id, 31);

    // Get draft data for this form
    $draft_data = _accua_forms_get_draft_data($fid);

    if (empty($draft_data['fields'])) {
      die('-1');
    }

    $old_fields = $draft_data['fields'];
    unset($draft_data['fields']);
    $new_fields = array();

    $order = explode(',', $order);

    foreach ($order as $i) {
      $i = preg_replace('/^(new-)?widget-\\d+_/', '', $i);
      if (isset($old_fields[$i])) {
        $new_fields[$i] = $old_fields[$i];
        unset($old_fields[$i]);
      }
    }

    if($old_fields){
      $new_fields += $old_fields;
    }
    $draft_data['fields'] = $new_fields;

    // Save to draft (not to live database)
    _accua_forms_save_draft($fid, $draft_data);
  }

  die('1');
}

add_action( 'wp_ajax_accua-save-form-field', 'accua_forms_save_form_field');
/* azione dove vengono salvati i campi dei un form - saves to draft */
function accua_forms_save_form_field() {
  if (!current_user_can('manage_options')){
    wp_die( -1, 403 );
  }
  check_ajax_referer('edit_form', '_nonce_edit_form');

  $post = stripslashes_deep($_POST);

  $fid = $post['form-id'];
  if (accua_forms_validate_form_id($fid) !== '') {
    die('-1');
  }

  // Get draft data instead of live data
  $draft_data = _accua_forms_get_draft_data($fid);
  if (empty($draft_data['fields'])) {
    $draft_data['fields'] = array();
  }

  $avail_fields = get_option('accua_forms_avail_fields', array());
  @ $wid = (string) $post['widget-id'];
  if (isset($avail_fields[$wid])) {
    $check_ref = $wid;
  } else {
    if (preg_match('/^(__html|__fieldset-begin|__fieldset-end)-\d+$/', $wid, $matches)) {
      $check_ref = $matches[1];
    } else {
      die('-1');
    }
  }
  if (empty($post['delete_widget'])) {
    @ $ref = $post['id_base'];
    if ($ref !== $check_ref) {
      die('-1');
    }
    $required = !empty($post["form-field-{$wid}-required"]);
    $widget_number = empty($post['multi_number']) ? (empty($post['widget_number']) ? '' : (int)$post['widget_number']) : (int)$post['multi_number'];

    if (isset($draft_data['fields'][$wid])) {
      $old_istance_data = $draft_data['fields'][$wid];
    } else {
      $old_istance_data = array();
    }

    $draft_data['fields'][$wid] = array (
      'version' => 2,
      'istance_id' => $wid,
      'widget_number' => $widget_number,
      'ref' => $ref,
      'required' => $required,
    );

    if (!empty($post["form-field-{$wid}-override-label"])) {
      @ $label = (string) $post["form-field-{$wid}-label"];
      if (!current_user_can('unfiltered_html')) {
        $label = wp_kses($label, 'post');
      }
      $draft_data['fields'][$wid]['label'] = $label;
    }

    $is_file = false;
    $is_date = false;
    if (isset($avail_fields[$wid]['type'])) {
      if ($avail_fields[$wid]['type'] == 'file') {
        $is_file = true;
      } elseif ($avail_fields[$wid]['type'] == 'date') {
        $is_date = true;
      }
    }

    if (!empty($post["form-field-{$wid}-override-default-value"])) {
      @ $default_value = (string) $post["form-field-{$wid}-default-value"];
      if ($is_date) {
        $default_value = accua_forms_filter_date($default_value);
      } elseif (!current_user_can('unfiltered_html')) {
        //This is filtered in any case because field type can change
        $default_value = wp_kses($default_value, 'post');
      }
      $draft_data['fields'][$wid]['default_value'] = $default_value;
    }

    if (!empty($post["form-field-{$wid}-override-allowed-values"])) {
      @ $allowed_values = (string) $post["form-field-{$wid}-allowed-values"];
      if ($is_file){
        $draft_data['fields'][$wid]['allowed_extensions'] = accua_forms_filter_extensions($allowed_values);
      } else {
        $draft_data['fields'][$wid]['allowed_values'] = $allowed_values;
      }
    }
    if (!empty($post["form-field-{$wid}-override-datemin-values"])) {
      @ $mindate_values = (string) $post["form-field-{$wid}-min-of-date"];
      $draft_data['fields'][$wid]['min_date'] = accua_forms_filter_date($mindate_values);
    }
    if (!empty($post["form-field-{$wid}-override-datemax-values"])) {
      @ $maxdate_values = (string) $post["form-field-{$wid}-max-of-date"];
      $draft_data['fields'][$wid]['max_date'] = accua_forms_filter_date($maxdate_values);
    }
    
    // Save post_type for post-select and post-multicheckbox fields
    if (isset($post["form-field-{$wid}-post-type"])) {
      @ $post_type_value = (string) $post["form-field-{$wid}-post-type"];
      // Validate post type
      $valid_post_types = get_post_types(array('public' => true));
      if (isset($valid_post_types[$post_type_value])) {
        $draft_data['fields'][$wid]['post_type'] = $post_type_value;
      }
    }
    
    // Save country_code for telephone fields (for libphonenumber validation)
    if (isset($post["form-field-{$wid}-country-code"])) {
      $country_code = strtoupper(sanitize_text_field($post["form-field-{$wid}-country-code"]));
      // Validate against the list of countries
      $valid_countries = accua_forms_get_countries();
      if (isset($valid_countries[$country_code])) {
        $draft_data['fields'][$wid]['country_code'] = $country_code;
      }
    }

    /**
     * Filter field instance data before saving to draft.
     *
     * @param array  $field_instance The field instance data being saved.
     * @param string $widget_id      The field widget ID.
     * @param array  $post_data      The raw POST data (already stripslashed).
     * @param array  $field_def      The field definition from avail_fields.
     */
    $draft_data['fields'][$wid] = apply_filters(
      'accua_forms_save_field_data',
      $draft_data['fields'][$wid],
      $wid,
      $post,
      isset($avail_fields[$wid]) ? $avail_fields[$wid] : array()
    );

    // Save custom CSS class for the field wrapper
    if (isset($post["form-field-{$wid}-css-class"])) {
      $css_class_raw = sanitize_text_field($post["form-field-{$wid}-css-class"]);
      if ($css_class_raw !== '') {
        // Sanitize each class individually
        $classes = array_filter(array_map('sanitize_html_class', explode(' ', $css_class_raw)));
        $draft_data['fields'][$wid]['css_class'] = implode(' ', $classes);
      } else {
        $draft_data['fields'][$wid]['css_class'] = '';
      }
    }

    // Save custom CSS ID for the field wrapper
    if (isset($post["form-field-{$wid}-css-id"])) {
      $css_id_raw = sanitize_text_field($post["form-field-{$wid}-css-id"]);
      $draft_data['fields'][$wid]['css_id'] = sanitize_html_class($css_id_raw);
    }

    // Save fieldset style (fieldset-begin only)
    if (isset($post["form-field-{$wid}-fieldset-style"])) {
      $allowed_fieldset_styles = array(
        'border-off-title-off', 'border-on-title-off',
        'border-on-title-inline', 'border-on-title-outside',
        'border-on-title-inside', 'border-off-title-on',
      );
      $fs = sanitize_text_field($post["form-field-{$wid}-fieldset-style"]);
      if (in_array($fs, $allowed_fieldset_styles, true)) {
        $draft_data['fields'][$wid]['fieldset_style'] = $fs;
      }
    }

    // Save custom required message override
    if (!empty($post["form-field-{$wid}-override-required-msg"])) {
      $draft_data['fields'][$wid]['custom_required_message'] = sanitize_text_field($post["form-field-{$wid}-custom-required-msg"]);
    }

    // Save custom format message override (email/phone)
    if (!empty($post["form-field-{$wid}-override-format-msg"])) {
      $draft_data['fields'][$wid]['custom_format_message'] = sanitize_text_field($post["form-field-{$wid}-custom-format-msg"]);
    }
  } else {
    unset($draft_data['fields'][$wid]);
  }

  // Save to draft (not to live database)
  _accua_forms_save_draft($fid, $draft_data);

  die('1');
}

function accua_forms_filter_text($text) {
  if ( is_object( $text ) || is_array( $text ) ) {
    return '';
  }

  $text = (string) $text;
  $text = wp_check_invalid_utf8( $text );
  $text = preg_replace( '/[ \t\n\r\0\x0B]+/', ' ', $text );
  $text = trim( $text );

  return $text;
}

function accua_forms_filter_email($email) {
  $parts = explode('<', $email);
  if (isset($parts[1])) {
    $email = accua_forms_filter_text(trim($parts[1], "<> \t\n\r\0\x0B"));
    $display_name = accua_forms_filter_text(trim($parts[0]));
    return "$display_name <$email>";
  } else {
    return accua_forms_filter_text($email);
  }
}

function accua_forms_filter_emails($emails) {
  $split_emails = preg_split("/\s*[,;]\s*/", $emails);
  $emails = array();
  foreach ($split_emails as $email) {
    $emails[] = accua_forms_filter_email($email);
  }
  return implode(', ', $emails);
}

function accua_forms_filter_extensions($extensions) {
  $cleaned_extensions = array();
  $mimes = get_allowed_mime_types();
  $extensions = explode("\n", $extensions);
  foreach ($extensions as $extension) {
    $extension = strtolower( trim( ltrim( trim( $extension ), '.' ) ) );
    if ($extension !== '') {
      foreach ( $mimes as $ext_preg => $mime_match ) {
        $ext_preg = '!^' . $ext_preg . '$!i';
        if ( preg_match( $ext_preg, $extension ) ) {
          $cleaned_extensions[] = $extension;
          break;
        }
      }
    }
  }
  return implode("\n", $cleaned_extensions);
}

function accua_forms_filter_settings($form_settings) {
  foreach($form_settings as $k => $v) {
    switch ($k) {
      case 'success_message_no_message':
      case 'error_message_no_message':
      case 'admin_emails_message_no_message':
      case 'confirmation_emails_message_no_message':
      case 'use_ajax':
        //boolean
        $form_settings[$k] = (bool) $v;
      break;
      case 'layout':
        // Only set if valid layout value, otherwise remove to use default
        if ($v === 'toplabel' || $v === 'inlinelabel' || $v === 'sidebyside') {
          $form_settings[$k] = $v;
        } else {
          unset($form_settings[$k]); // Reset to default
        }
      break;
      case 'emails_from':
        // single email
        $form_settings[$k] = accua_forms_filter_email($v);
      break;
      case 'admin_emails_to':
      case 'emails_bcc':
        //emails list
        $form_settings[$k] = accua_forms_filter_email($v);
      break;
      case 'success_message':
      case 'error_message':
      case 'admin_emails_message':
      case 'confirmation_emails_message':
        //HTML
        if (!current_user_can('unfiltered_html')) {
          $form_settings[$k] = wp_kses($v, 'post');
        }
      break;
      //case 'title':
      //case 'emails_from_name':
      //case 'admin_emails_subject':
      //case 'confirmation_emails_subject':
      //case 'style_*':
      default:
        // text field
        $form_settings[$k] = accua_forms_filter_text($v);
    }
  }
  return $form_settings;
}

/**
 * AJAX handler to restore default message values.
 *
 * Restores the default content for a specific message section:
 * - success_message: On-screen success message
 * - error_message: On-screen error message
 * - admin_emails: Admin notification email (subject + message only)
 * - confirmation_emails: Confirmation email (subject + message only)
 *
 * @since 2.0.0-beta.6
 */
add_action('wp_ajax_accua_forms_restore_default_message', 'accua_forms_restore_default_message');
function accua_forms_restore_default_message() {
  if (!current_user_can('manage_options')) {
    wp_send_json_error(array('message' => __('Permission denied.', 'contact-forms')), 403);
  }

  check_ajax_referer('accua_forms_restore_default', 'nonce');

  $message_type = isset($_POST['message_type']) ? sanitize_key($_POST['message_type']) : '';

  // Get default values
  $defaults = accua_forms_get_default_form_data();

  // Define which fields to restore for each message type
  $restore_map = array(
    'success_message' => array('success_message'),
    'error_message' => array('error_message'),
    'admin_emails' => array('admin_emails_subject', 'admin_emails_message'),
    'confirmation_emails' => array('confirmation_emails_subject', 'confirmation_emails_message'),
  );

  if (!isset($restore_map[$message_type])) {
    wp_send_json_error(array('message' => __('Invalid message type.', 'contact-forms')), 400);
  }

  // Get current form data
  $form_data = get_option('accua_forms_default_form_data', array());
  if (!is_array($form_data)) {
    $form_data = array();
  }

  // Restore the specified fields
  $restored_values = array();
  foreach ($restore_map[$message_type] as $field) {
    $form_data[$field] = $defaults[$field];
    $restored_values[$field] = $defaults[$field];
  }

  // Save updated form data
  update_option('accua_forms_default_form_data', $form_data);

  wp_send_json_success(array(
    'message' => __('Default values restored successfully.', 'contact-forms'),
    'values' => $restored_values,
  ));
}

add_action( 'wp_ajax_accua-save-form-settings', 'accua_forms_save_form_settings');
function accua_forms_save_form_settings() {
  if (!current_user_can('manage_options')){
    wp_die( -1, 403 );
  }
  check_ajax_referer('edit_form', '_nonce_edit_form');

  $post = stripslashes_deep($_POST);

  $fid = $post['form-id'];
  if (accua_forms_validate_form_id($fid) !== '') {
    die('-1');
  }

  // Get draft data instead of live data
  $draft_data = _accua_forms_get_draft_data($fid);

  $settings = array(
    'title',
    'success_message',
    'success_message_no_message',
    'error_message',
    'error_message_no_message',
    'emails_from_name',
    'emails_from',
    'admin_emails_to',
    'emails_bcc',
    'admin_emails_subject',
    'admin_emails_message',
    'admin_emails_message_no_message',
    'confirmation_emails_subject',
    'confirmation_emails_message',
    'confirmation_emails_message_no_message',
    'gads_conversion_tracking_code',
    //'use_ajax',

    'layout',
    '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_value',
    'submission_retention_unit',
    'submission_retention_mode',
  );

  // print_r($post);

  $new_form_settings = array();
  foreach($settings as $i) {
    if (isset($post[$i])) {
      $new_form_settings[$i] = $post[$i];
    }
    if (isset($draft_data[$i])) {
      unset($draft_data[$i]);
    }
  }

  $draft_data += accua_forms_filter_settings($new_form_settings);

  $draft_data['use_ajax'] = !empty($post['use_ajax']);
  $draft_data['submission_retention_override'] = !empty($post['submission_retention_override']);

  // Save to draft (not to live database)
  _accua_forms_save_draft($fid, $draft_data);

  // Return JSON response for AJAX handler
  wp_send_json_success($draft_data);
}

/**
 * AJAX handler to publish draft to live database.
 * Called when user clicks the Save button.
 */
add_action( 'wp_ajax_accua-publish-form-draft', 'accua_forms_publish_form_draft');
function accua_forms_publish_form_draft() {
  if (!current_user_can('manage_options')){
    wp_die( -1, 403 );
  }
  check_ajax_referer('edit_form', '_nonce_edit_form');

  $post = stripslashes_deep($_POST);
  $fid = isset($post['form-id']) ? $post['form-id'] : '';
  
  if (accua_forms_validate_form_id($fid) !== '') {
    wp_send_json_error(array('message' => __('Invalid form ID.', 'contact-forms')), 400);
  }

  // Publish the draft
  $result = _accua_forms_publish_draft($fid);

  if ($result) {
    wp_send_json_success(array('message' => __('Form saved successfully.', 'contact-forms')));
  } else {
    // Draft might not exist (nothing to publish) - this is OK for a new form
    // Check if form exists in database
    $forms_data = get_option('accua_forms_saved_forms', array());
    if (isset($forms_data[$fid])) {
      wp_send_json_success(array('message' => __('No changes to save.', 'contact-forms')));
    } else {
      wp_send_json_error(array('message' => __('Failed to save form.', 'contact-forms')), 500);
    }
  }
}

/**
 * AJAX handler to discard draft and reload from published data.
 * Called when user clicks "Discard changes".
 */
add_action( 'wp_ajax_accua-discard-form-draft', 'accua_forms_discard_form_draft');
function accua_forms_discard_form_draft() {
  if (!current_user_can('manage_options')){
    wp_die( -1, 403 );
  }
  check_ajax_referer('edit_form', '_nonce_edit_form');

  $post = stripslashes_deep($_POST);
  $fid = isset($post['form-id']) ? $post['form-id'] : '';
  
  if (accua_forms_validate_form_id($fid) !== '') {
    wp_send_json_error(array('message' => __('Invalid form ID.', 'contact-forms')), 400);
  }

  // Delete the draft
  _accua_forms_delete_draft($fid);

  wp_send_json_success(array('message' => __('Changes discarded.', 'contact-forms')));
}

/**
 * Filter an admin-configured post_status value for post fields down to the
 * statuses those fields may expose in a public dropdown: publish and private.
 *
 * Draft/pending/future content is never exposed, regardless of configuration.
 *
 * @since 2.2.27
 * @param string|array $post_status Comma-separated string or array of statuses.
 * @return array Allowed statuses (may be empty).
 */
function accua_forms_filter_field_post_status($post_status) {
  if (!is_array($post_status)) {
    $post_status = explode(',', (string) $post_status);
  }
  $post_status = array_map('trim', $post_status);
  return array_values(array_intersect($post_status, array('publish', 'private')));
}

/**
 * Check whether an extra_args string received from the AJAX endpoint matches a
 * post-select / post-multicheckbox configuration actually stored by an admin,
 * for the post type the request resolved to.
 *
 * The extra_args string is echoed into the form markup and sent back by the
 * browser, so it is client-controlled. Privileged parameters (post_status=private)
 * are only honored when the exact string exists in a saved field configuration —
 * otherwise any visitor could craft a request that enumerates private post titles.
 * The post type is part of the match: a query string saved for one post type must
 * not unlock private posts of a different type (the field's post type lives in a
 * separate setting, so the string alone does not identify what it exposes).
 *
 * @since 2.2.27
 * @param string $extra_args Sanitized extra_args string from the request.
 * @param string $post_type  Post type the request resolved to (after the
 *                           post_type override inside extra_args, if any).
 * @return bool True when a stored field configuration matches both.
 */
function accua_forms_extra_args_is_saved_config($extra_args, $post_type) {
  $extra_args = trim($extra_args);
  if ($extra_args === '') {
    return false;
  }

  // Candidate configurations: array of (allowed_values, configured post type).
  $candidates = array();
  $avail_fields = get_option('accua_forms_avail_fields', array());
  foreach ($avail_fields as $field) {
    if (!empty($field['type']) && ($field['type'] === 'post-select' || $field['type'] === 'post-multicheckbox') && isset($field['allowed_values'])) {
      $candidates[] = array($field['allowed_values'], isset($field['post_type']) ? $field['post_type'] : 'page');
    }
  }
  $forms = get_option('accua_forms_saved_forms', array());
  foreach ($forms as $form) {
    if (empty($form['fields']) || !is_array($form['fields'])) {
      continue;
    }
    foreach ($form['fields'] as $inst) {
      if (!is_array($inst) || empty($inst['ref']) || !isset($avail_fields[$inst['ref']]['type'])) {
        continue;
      }
      $type = $avail_fields[$inst['ref']]['type'];
      if (($type === 'post-select' || $type === 'post-multicheckbox') && isset($inst['allowed_values'])) {
        $inst_post_type = isset($inst['post_type']) ? $inst['post_type']
          : (isset($avail_fields[$inst['ref']]['post_type']) ? $avail_fields[$inst['ref']]['post_type'] : 'page');
        $candidates[] = array($inst['allowed_values'], $inst_post_type);
      }
    }
  }

  foreach ($candidates as $candidate) {
    list($candidate_args, $candidate_post_type) = $candidate;
    if (trim(sanitize_text_field($candidate_args)) !== $extra_args) {
      continue;
    }
    // Resolve the candidate's effective post type the same way the request
    // does: a post_type override inside the string wins over the field setting.
    $candidate_extra = array();
    wp_parse_str($extra_args, $candidate_extra);
    if (!empty($candidate_extra['post_type'])) {
      $candidate_post_type = sanitize_text_field($candidate_extra['post_type']);
    }
    if ($candidate_post_type === $post_type) {
      return true;
    }
  }
  return false;
}

/**
 * AJAX handler to get posts for post-select fields with pagination.
 * Available to both logged-in and anonymous users (for frontend forms).
 *
 * @since 2.0.0-beta.29
 */
add_action('wp_ajax_accua_forms_get_posts', 'accua_forms_ajax_get_posts');
add_action('wp_ajax_nopriv_accua_forms_get_posts', 'accua_forms_ajax_get_posts');
function accua_forms_ajax_get_posts() {
  // Verify nonce
  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verification
  if (!isset($_REQUEST['_nonce']) || !wp_verify_nonce($_REQUEST['_nonce'], 'accua_forms_get_posts')) {
    wp_send_json_error(array('message' => __('Security check failed.', 'contact-forms')), 403);
  }

  // Sanitize inputs
  $post_type = isset($_REQUEST['post_type']) ? sanitize_text_field(wp_unslash($_REQUEST['post_type'])) : 'page';
  $search = isset($_REQUEST['search']) ? sanitize_text_field(wp_unslash($_REQUEST['search'])) : '';
  $page = isset($_REQUEST['page']) ? absint($_REQUEST['page']) : 1;
  $per_page = isset($_REQUEST['per_page']) ? min(absint($_REQUEST['per_page']), 100) : 50;
  $extra_args = isset($_REQUEST['extra_args']) ? sanitize_text_field(wp_unslash($_REQUEST['extra_args'])) : '';
  $selected = isset($_REQUEST['selected']) ? sanitize_text_field(wp_unslash($_REQUEST['selected'])) : '';

  // Validate post type
  $valid_post_types = get_post_types(array('public' => true));
  if (!isset($valid_post_types[$post_type])) {
    $post_type = 'page';
  }

  // Calculate offset
  $offset = ($page - 1) * $per_page;

  // Statuses the response may contain (extended below when the field
  // configuration explicitly requests private posts).
  $allowed_statuses = array('publish');

  // Build query arguments
  $args = array(
    'post_type' => $post_type,
    'number'    => $per_page + 1, // Get one extra to check if there are more
    'offset'    => $offset,
    's'         => $search,
  );

  // Parse extra arguments (backward compatibility with allowed_values textarea)
  if (!empty($extra_args)) {
    // Parse the query string format
    $extra = array();
    wp_parse_str($extra_args, $extra);

    // Allow post_type override from extra_args (backward compatibility)
    if (isset($extra['post_type'])) {
      $override_post_type = sanitize_text_field($extra['post_type']);
      // Validate the overridden post type
      if (isset($valid_post_types[$override_post_type])) {
        $post_type = $override_post_type;
        $args['post_type'] = $post_type;
      }
    }

    // Merge only safe parameters
    $safe_params = array('meta_key', 'meta_value', 'authors', 'parent', 'child_of', 'exclude', 'include', 'sort_column', 'sort_order');
    foreach ($safe_params as $param) {
      if (isset($extra[$param])) {
        $args[$param] = $extra[$param];
      }
    }

    // post_status is a privileged parameter: only publish/private are ever
    // honored, and 'private' only when the extra_args string matches a field
    // configuration stored by an admin (or the user can read private posts,
    // e.g. the form editor preview). Otherwise a visitor could craft a request
    // that enumerates private post titles.
    if (!empty($extra['post_status'])) {
      $requested_statuses = accua_forms_filter_field_post_status($extra['post_status']);
      if (in_array('private', $requested_statuses, true)
          && !current_user_can('read_private_posts')
          && !accua_forms_extra_args_is_saved_config($extra_args, $post_type)) {
        $requested_statuses = array('publish');
      }
      if (!empty($requested_statuses)) {
        $args['post_status'] = $requested_statuses;
        $allowed_statuses = $requested_statuses;
      }
    }
  }

  // Get posts using WPML-compatible function
  $posts = accua_get_pages($args);

  // Check if there are more results
  $has_more = count($posts) > $per_page;
  if ($has_more) {
    array_pop($posts); // Remove the extra item
  }

  // Format results for the dropdown
  $results = array();
  foreach ($posts as $post) {
    $results[] = array(
      'id'   => $post->ID,
      'text' => $post->post_title,
    );
  }

  // If this is the first page and we have a selected value, ensure it's in the list
  if ($page === 1 && !empty($selected) && is_numeric($selected)) {
    $selected_id = absint($selected);
    $found = false;
    foreach ($results as $result) {
      if ($result['id'] === $selected_id) {
        $found = true;
        break;
      }
    }
    // If selected post not in results, fetch it separately and prepend.
    // Only statuses the field is allowed to expose (publish, plus private when
    // explicitly configured): this endpoint is available to anonymous visitors,
    // so it must not disclose titles of other drafts/private/pending posts.
    if (!$found) {
      $selected_post = get_post($selected_id);
      if ($selected_post && $selected_post->post_type === $post_type && in_array($selected_post->post_status, $allowed_statuses, true)) {
        array_unshift($results, array(
          'id'   => $selected_post->ID,
          'text' => $selected_post->post_title,
        ));
      }
    }
  }

  wp_send_json_success(array(
    'results' => $results,
    'more'    => $has_more,
    'page'    => $page,
  ));
}

/**
 * Get available public post types for the post-select field editor.
 *
 * @since 2.0.0-beta.29
 * @return array Array of post type slug => label pairs.
 */
function accua_forms_get_public_post_types() {
  $post_types = get_post_types(array('public' => true), 'objects');
  $options = array();
  foreach ($post_types as $post_type) {
    // Skip attachments
    if ($post_type->name === 'attachment') {
      continue;
    }
    $options[$post_type->name] = $post_type->labels->singular_name;
  }
  return $options;
}

function accua_forms_field_settings_form_counter() {
  static $i = 0;
  $i++;
  return $i;
}

/* impostazioni dei campi */
function accua_forms_field_text_settings_form($fid, $field_data=array(), $istance_data=array()){
  static $html_multi_number = 0;
  $i = accua_forms_field_settings_form_counter();

  $hidden = '';
  if (!is_array($istance_data)) {
    if ($istance_data === 'hidden') {
      $hidden = 'style="display:none"';
    }
    $istance_data = array();
    $empty_istance = true;
  } else {
    $empty_istance = empty($istance_data);
  }

  if (!is_array($field_data)){
    $field_data = array();
  }

  $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' => '',
  );

  $override_label = isset($istance_data['label']) ? 'checked="checked"' : '';
  $override_default_value = isset($istance_data['default_value']) ? 'checked="checked"' : '';
  $override_allowed_values = isset($istance_data['allowed_values']) ? 'checked="checked"' : '';
  $override_allowed_extensions = '';
  $override_custom_required_msg = isset($istance_data['custom_required_message']) ? 'checked="checked"' : '';
  $override_custom_format_msg = isset($istance_data['custom_format_message']) ? 'checked="checked"' : '';

  if ($field_data['type'] == 'file') {
    if (isset($istance_data['version']) && $istance_data['version'] >= 2) {
      if (isset($istance_data['allowed_extensions'])) {
        $override_allowed_extensions = 'checked="checked"';
      }
    } else {
      if (isset($istance_data['allowed_values'])) {
        $istance_data['allowed_extensions'] = $istance_data['allowed_values'];
        $override_allowed_extensions = 'checked="checked"';
      }
    }

    if ($field_data['version'] < 2) {
      $field_data['allowed_extensions'] = $field_data['allowed_values'];
    }

    if ($field_data['allowed_extensions'] === '') {
      $file_data = get_option('accua_forms_default_file_field_data',array());
      if (isset($file_data['valid_extensions'])){
        $field_data['allowed_extensions'] = $file_data['valid_extensions'];
      }
    }
  }

  if ($field_data['type'] == 'date') {
  	$override_mindate_values = isset($istance_data['min_date']) ? 'checked="checked"' : '';
	$override_maxdate_values = isset($istance_data['max_date']) ? 'checked="checked"' : '';
    $istance_data += array(
	  'min_date' => $field_data['min_date'],
	  'max_date' => $field_data['max_date'],
	  'default_value' => $field_data['default_date_value'],
	);
  }

  $istance_data += array(
    //'version' => 1,
    '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'],
    'allowed_extensions' => $field_data['allowed_extensions'],
    'required' => false,
    'post_type' => 'page', // Default post type for post-select fields
    'css_class' => '',
    'css_id' => '',
    'custom_required_message' => '',
    'custom_format_message' => '',
    'fieldset_style' => 'border-off-title-off',
  );

  foreach ($istance_data as $key => $value) {
    $istance_data[$key] = esc_attr($istance_data[$key]);
  }

  foreach ($field_data as $key => $value) {
    $field_data[$key] = esc_attr($field_data[$key]);
  }

  $multi_number = '';
  $add_new = '';

  if (in_array($istance_data['ref'], array('__html','__fieldset-begin','__fieldset-end'))) {
    $forceoverride_field = true;
    if ($empty_istance) {
      $add_new = 'multi';
      $istance_data['istance_id'] .= '-__i__';
      $istance_data['widget_number'] = 1;
      $multi_number = 1 + $html_multi_number;
    } else {
      if ($html_multi_number < $istance_data['widget_number']) {
        $html_multi_number = $istance_data['widget_number'];
      }
    }
  } else {
    $forceoverride_field = false;
    $add_new = $empty_istance ? 'single' : '';
  }

  $fid = esc_attr($fid);
  $testi_eot = array (
    'label' => __( 'Label', 'contact-forms'),
    'override' => __( 'override', 'contact-forms'),
    'default_value' => __( 'Default value', 'contact-forms'),
    'default_values' => __( 'Default value(s)', 'contact-forms'),
    'desc_def' => __( 'For multiple default values, use | as separator.', 'contact-forms'),
    'allowed_values' => __( 'Allowed values', 'contact-forms'),
    'desc_all' => __( 'The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database. The label is optional, and the key will be used as the label if no label is specified.', 'contact-forms'),
    'allowed_extensions' => __( 'Allowed extensions', 'contact-forms'),
    'desc_all_ext' => __( 'Accepted file extensions. One per line, without dots.', 'contact-forms'),
    'required' => __( 'Required', 'contact-forms'),
    'custom_HTML_content' => __( 'Custom HTML content', 'contact-forms'),
    'refresh_preview' => __( 'Refresh Preview', 'contact-forms'),
    'add' => __( 'Add field', 'contact-forms'),
    'remove' => __( 'Remove', 'contact-forms'),
    'close' => __( 'Close', 'contact-forms'),
    'save' => __( 'Save', 'contact-forms'),
    'min-of-date' => __( 'Min date', 'contact-forms'),
    'max-of-date' => __( 'Max date', 'contact-forms')
  );

  if ($forceoverride_field) {
    $override_begin = '';
    $override_type = 'hidden';
    $override_end = '';
  } else {
    $override_begin = "({$testi_eot['override']}: ";
    $override_type = 'checkbox';
    $override_end = ')';
  }

  // phpcs:disable PluginCheck.CodeAnalysis.Heredoc.NotAllowed, WordPress.Security.EscapeOutput.HeredocOutputNotEscaped -- Heredoc used for HTML templates with pre-escaped variables
  $content = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-label">{$testi_eot['label']}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-label" value="1" {$override_label} />{$override_end}<br>
    <input type="text" value="{$istance_data['label']}" name="form-field-{$istance_data['istance_id']}-label" id="widget-{$istance_data['istance_id']}-label" class="widefat"></p>
EOT;

  $default_value = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-default-value">{$testi_eot['default_value']}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-default-value" value="1" {$override_default_value} />{$override_end}<br>
    <input type="text" value="{$istance_data['default_value']}" name="form-field-{$istance_data['istance_id']}-default-value" id="widget-{$istance_data['istance_id']}-default-value" class="widefat"></p>
EOT;

  $default_values = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-default-value">{$testi_eot['default_value']}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-default-value" value="1" {$override_default_value} />{$override_end}<br>
    <input type="text" value="{$istance_data['default_value']}" name="form-field-{$istance_data['istance_id']}-default-value" id="widget-{$istance_data['istance_id']}-default-value" class="widefat"><br />
    </p>
EOT;

  $allowed_values = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-allowed-values">{$testi_eot['allowed_values']}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-allowed-values" value="1" {$override_allowed_values} />{$override_end}<br>
    <textarea rows="6" cols="50" name="form-field-{$istance_data['istance_id']}-allowed-values" id="widget-{$istance_data['istance_id']}-allowed-values" class="widefat">{$istance_data['allowed_values']}</textarea><br />
    {$testi_eot['desc_all']}</p>
EOT;

  $allowed_ext = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-allowed-values">{$testi_eot['allowed_extensions']}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-allowed-values" value="1" {$override_allowed_extensions} />{$override_end}<br>
    <textarea rows="6" cols="50" name="form-field-{$istance_data['istance_id']}-allowed-values" id="widget-{$istance_data['istance_id']}-allowed-values" class="widefat">{$istance_data['allowed_extensions']}</textarea><br />
    {$testi_eot['desc_all_ext']}</p>
EOT;

  $required_checked = empty($istance_data['required']) ? '' : 'checked="checked"';
  $required = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-required">{$testi_eot['required']}:</label>
    <input type="checkbox" value="1" {$required_checked} name="form-field-{$istance_data['istance_id']}-required" id="widget-{$istance_data['istance_id']}-required"></p>
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 = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-custom-required-msg">{$custom_required_msg_label}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-required-msg" value="1" {$override_custom_required_msg} />{$override_end}<br>
    <input type="text" value="{$istance_data['custom_required_message']}" name="form-field-{$istance_data['istance_id']}-custom-required-msg" id="widget-{$istance_data['istance_id']}-custom-required-msg" class="widefat"><br>
    <small>{$custom_required_msg_desc}</small></p>
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 = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-custom-format-msg">{$custom_format_msg_label}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-format-msg" value="1" {$override_custom_format_msg} />{$override_end}<br>
    <input type="text" value="{$istance_data['custom_format_message']}" name="form-field-{$istance_data['istance_id']}-custom-format-msg" id="widget-{$istance_data['istance_id']}-custom-format-msg" class="widefat"><br>
    <small>{$custom_format_msg_desc}</small></p>
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 = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-custom-format-msg">{$custom_format_msg_label}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-format-msg" value="1" {$override_custom_format_msg} />{$override_end}<br>
    <input type="text" value="{$istance_data['custom_format_message']}" name="form-field-{$istance_data['istance_id']}-custom-format-msg" id="widget-{$istance_data['istance_id']}-custom-format-msg" class="widefat"><br>
    <small>{$custom_format_msg_desc}</small></p>
EOT;
  }

  if ($field_data['type'] == 'date'){
    $default_date_value = <<<EOT
      <p><label for="widget-{$istance_data['istance_id']}-default-value">{$testi_eot['default_value']}:</label>
      {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-default-value" value="1" {$override_default_value} />{$override_end}<br>
      <input type="date" value="{$istance_data['default_value']}" name="form-field-{$istance_data['istance_id']}-default-value" id="widget-{$istance_data['istance_id']}-default-value" class="widefat"></p>
EOT;
    $min_date = <<<EOT
      <p><label for="widget-{$istance_data['istance_id']}-min-of-date">{$testi_eot['min-of-date']}:</label>
      {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-datemin-values" value="1" {$override_mindate_values} />{$override_end}<br>
      <input type="date" value="{$istance_data['min_date']}" name="form-field-{$istance_data['istance_id']}-min-of-date" id="widget-{$istance_data['istance_id']}-min-of-date"></p>
EOT;
    $max_date = <<<EOT
      <p><label for="widget-{$istance_data['istance_id']}-max-of-date">{$testi_eot['max-of-date']}:</label>
      {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-datemax-values" value="1" {$override_maxdate_values} />{$override_end}<br>
      <input type="date" value="{$istance_data['max_date']}" name="form-field-{$istance_data['istance_id']}-max-of-date" id="widget-{$istance_data['istance_id']}-max-of-date"></p>
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 .= '<option value="' . esc_attr($pt_slug) . '"' . $selected . '>' . esc_html($pt_label) . '</option>';
    }
    $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 = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-post-type">{$post_type_label}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-post-type" value="1" {$override_post_type} />{$override_end}<br>
    <select name="form-field-{$istance_data['istance_id']}-post-type" id="widget-{$istance_data['istance_id']}-post-type" class="widefat">{$post_type_options}</select><br />
    {$post_type_desc}</p>
    <p><label for="widget-{$istance_data['istance_id']}-allowed-values">{$query_params_label}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-allowed-values" value="1" {$override_allowed_values} />{$override_end}<br>
    <textarea rows="3" cols="50" name="form-field-{$istance_data['istance_id']}-allowed-values" id="widget-{$istance_data['istance_id']}-allowed-values" class="widefat">{$istance_data['allowed_values']}</textarea><br />
    {$query_params_desc}</p>
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 .= '<option value="' . esc_attr($code) . '"' . $selected . '>' . esc_html($country_name) . '</option>';
    }
    $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 = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-country-code">{$country_label}:</label>
    {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-country-code" value="1" {$override_country} />{$override_end}<br>
    <select name="form-field-{$istance_data['istance_id']}-country-code" id="widget-{$istance_data['istance_id']}-country-code" class="widefat">{$country_options}</select><br />
    {$country_desc}</p>
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 = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-css-class">{$css_class_label}:</label><br>
    <input type="text" value="{$istance_data['css_class']}" name="form-field-{$istance_data['istance_id']}-css-class" id="widget-{$istance_data['istance_id']}-css-class" class="widefat"><br>
    <small>{$css_class_desc}</small></p>
EOT;
  $css_id_field = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-css-id">{$css_id_label}:</label><br>
    <input type="text" value="{$istance_data['css_id']}" name="form-field-{$istance_data['istance_id']}-css-id" id="widget-{$istance_data['istance_id']}-css-id" class="widefat"><br>
    <small>{$css_id_desc}</small></p>
EOT;

  switch ($field_data['type']) {
    case 'textarea':
      $content .= <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-default-value">{$testi_eot['default_value']}:</label>
      {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-default-value" value="1" {$override_default_value} />{$override_end}<br>
      <textarea rows="6" cols="50" name="form-field-{$istance_data['istance_id']}-default-value" id="widget-{$istance_data['istance_id']}-default-value" class="widefat">{$istance_data['default_value']}</textarea></p>
    $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 .= '<option value="' . esc_attr($opt_val) . '"' . $opt_selected . '>' . esc_html($opt_label) . '</option>';
      }
      $content = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-label">{$gt_label_text}:</label><br>
    <input type="hidden" name="form-field-{$istance_data['istance_id']}-override-label" value="1">
    <input type="text" value="{$istance_data['label']}" name="form-field-{$istance_data['istance_id']}-label" id="widget-{$istance_data['istance_id']}-label" class="widefat"><br>
    <small>{$gt_desc_text}</small></p>
    <p><label for="widget-{$istance_data['istance_id']}-fieldset-style">{$fs_label_text}:</label><br>
    <select name="form-field-{$istance_data['istance_id']}-fieldset-style" id="widget-{$istance_data['istance_id']}-fieldset-style" class="widefat accua-fieldset-style-select">{$fs_options_html}</select></p>
EOT;
      $content .= $css_class_field . $css_id_field;
    break;
    case 'fieldset-end':
      //Nothing!
      $content = '';
    break;
    case 'html':
      $content = <<<EOT
    <p><label for="widget-{$istance_data['istance_id']}-default-value">{$testi_eot['custom_HTML_content']}</label>
      {$override_begin}<input type="{$override_type}" name="form-field-{$istance_data['istance_id']}-override-default-value" value="1" {$override_default_value} />{$override_end}<br>
      <textarea rows="6" cols="50" name="form-field-{$istance_data['istance_id']}-default-value" id="widget-{$istance_data['istance_id']}-default-value" class="widefat">{$istance_data['default_value']}</textarea></p>
    <p><a href="#" class="accua-refresh-preview">{$testi_eot['refresh_preview']}</a></p>
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 '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();
      $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 <<<EOT
<div class="widget ui-draggable" id="widget-{$i}_{$istance_data['istance_id']}" data-field-type="{$field_data['type']}" $hidden>  <div class="widget-top">
  <div class="widget-title-action">
    <a href="#add-field" class="widget-add-action hide-if-no-js" title="{$testi_eot['add']}" aria-label="{$testi_eot['add']}"></a>
    <a href="#available-widgets" class="widget-action hide-if-no-js"></a>
  </div>
  <div class="widget-title"><h4>{$field_data['name']}<span class="in-widget-title"></span></h4></div>
  </div>

  <div class="widget-inside">
  <form method="post" action="">
  <div class="widget-content">
    $content
  </div>
  <input type="hidden" value="{$fid}" name="form-id">
  <input type="hidden" value="{$istance_data['istance_id']}" class="widget-id" name="widget-id">
  <input type="hidden" value="{$field_data['id']}" class="id_base" name="id_base">
  <input type="hidden" value="250" class="widget-width" name="widget-width">
  <input type="hidden" value="200" class="widget-height" name="widget-height">
  <input type="hidden" value="{$istance_data['widget_number']}" class="widget_number" name="widget_number">
  <input type="hidden" value="{$multi_number}" class="multi_number" name="multi_number">
  <input type="hidden" value="{$add_new}" class="add_new" name="add_new">

  <div class="widget-control-actions">
    <div class="alignleft">
    <a href="#remove" class="widget-control-remove delete">{$testi_eot['remove']}</a> |
    <a href="#close" class="widget-control-close">{$testi_eot['close']}</a>
    </div>
   <div  class="alignright">
      <input type="submit" value="{$testi_eot['save']}" class="button button-primary widget-control-save accua-field-save-btn" id="widget-{$istance_data['istance_id']}-savewidget" name="savewidget">
    </div>
    <br class="clear">
  </div>
  </form>
  </div>

  <!--<div class="widget-description">
    {$field_data['description']}
  </div>-->
</div>

EOT;
  // phpcs:enable PluginCheck.CodeAnalysis.Heredoc.NotAllowed, WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
}


// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function, underscore prefix indicates private
function _accua_forms_get_abs_dest_path($dest_path = '') {
  if ($dest_path === '') {
    return realpath(ABSPATH) . '/wp-content/uploads/accua-forms';
  } elseif (substr($dest_path,0,1) === '/') {
    return $dest_path;
  } else {
    return realpath(ABSPATH) . '/' . $dest_path;
  }
}

// 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 );
}

/**
 * Check if a draft exists and differs from published data.
 * Used to show "unsaved changes" warning.
 * 
 * @param string|int $fid Form ID.
 * @return bool True if draft exists and differs from published.
 */
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function
function _accua_forms_has_unsaved_draft( $fid ) {
  $draft_key = _accua_forms_get_draft_key( $fid );
  $draft_data = get_transient( $draft_key );
  
  if ( $draft_data === false ) {
    return false;
  }
  
  // Compare with published data
  $forms_data = get_option( 'accua_forms_saved_forms', array() );
  $published_data = isset( $forms_data[ $fid ] ) ? $forms_data[ $fid ] : array();
  
  // Deep comparison (serialize for simplicity)
  // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize -- Used for comparison only
  return serialize( $draft_data ) !== serialize( $published_data );
}

// 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 '<!-- fid = ';
    print_r($fid);
    echo "\n\nform_data = ";
    print_r($form_data);
    echo "\n-->";
    */
    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'] : '';

        // 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_props = array();
            if ( !empty($field_properties['wrapperCssId']) ) {
              $hidden_props['id'] = $field_properties['wrapperCssId'];
            }
            $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 .= ' <strong>*</strong>';
            }
            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;
            $element = new Element_Button($istance_data['label'], 'submit', $submit_properties+array('name' => $istance_data['istance_id'], 'value' => $istance_data['default_value']));
          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" => "");
              $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<!-- ReCaptcha 1 is discontinued, please go to Contact Forms settings page and set reCaptcha v2 keys -->\n\n");
              } else {
                $element = new AccuaForm_Element_Captcha2 ($istance_data['label'], '', $field_properties+$captcha_properties);
              }
          break;
          case 'turnstile':
            $element = new AccuaForm_Element_Turnstile($istance_data['label'], $istance_data['istance_id'], $field_properties+array("description" => ""));
          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'])) {
            $element->setClass('accuaforms-field-required');
            if ($field_data['type'] === 'captcha' && empty($captcha_use_v1)) {
              //nothing
            } elseif ($field_data['type'] === 'turnstile') {
              //nothing - turnstile 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;
      }
      if ($add_submit) {
        $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("</td></tr>\n<tr>\n<td style='white-space:nowrap;vertical-align:top;padding:4px 10px 4px 0;'>",$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;
}

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();

      $afs_stats = _accua_forms_json_encode(array(
        'user_agent' => $form->stats['user_agent'],
        'platform' => $form->stats['platform'],
        'tentatives' => $form->stats['tentatives'],
        'submit_method' => $form->stats['submit_method'],
      ));

      $anonymized_ip = accua_forms_anonymize_ip($form->stats['ip']);

      // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Form submission insert requires direct query
      $insert_ret = $wpdb->insert(
        $wpdb->prefix . 'accua_forms_submissions',
        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 ($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 "<!-- istance_data: "
          , print_r($istance_data, true)
          , "\nfield_data: "
          , print_r($field_data, true)
          , "\nvalue: "
          , print_r($value, true)
          , "\n-->\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']] = "<strong style='font-size:14px;'>{$fieldset_label}</strong></td><td class='valori_submitted'>";
          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']] = "<strong>{$istance_data['istance_id']}</strong></td><td class='valori_submitted'><a href='".esc_url($file_download_url)."'>".esc_html($value)."</a>";
          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']] = "<strong>{$istance_data['istance_id']}</strong></td><td class='valori_submitted'><a href='mailto:".esc_attr($value)."'>".esc_html($value)."</a>";
            break;
          case 'submit':
          break;
          case 'colorpicker':
            $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;
            if ($value === '') {
              $value_html = '';
            } else {
              $value_esc = esc_attr($value);
              $value_html = "<span style='color: $value_esc'><font color='$value_esc'>&#9608;</font></span> $value_esc";
            }
            $replace_map['__submitted_html_raw'][$istance_data['istance_id']] = "<strong>{$istance_data['istance_id']}</strong></td><td class='valori_submitted'>$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']] = "<strong>{$istance_data['istance_id']}</strong></td><td class='valori_submitted'>".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 = "<table style='width:100%;border-collapse:collapse;'>\n<tr>\n<td style='white-space:nowrap;vertical-align:top;padding:4px 10px 4px 0;'>" . $replace_map['__submitted_html'] . "</td></tr></table>";
      $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 ($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'],'<html><head></head><body style="background:#f9f8f8;font-size: 12px;font-family: &quot;Lucida Sans&quot;,&quot;Lucida Grande&quot;, Verdana, Arial, Sans-Serif;">'.wpautop($form_data_replaced['admin_emails_message']).'</body></html>', $header);
        if (!$mail1) {
          $mail_success = false;
        }
      }

      if ($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'], '<html><head></head><body>'.wpautop($confirmation_emails_message).'</body></html>', $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));
          }
        }
      }

      /*
      echo "<!-- replace_map: "
         , print_r($replace_map, true)
         , "\nreplace_map: "
         , print_r($replace_map, true)
         , "\nform_data_replaced: "
         , print_r($form_data_replaced, true)
         , "\nautoreply_email: "
         , print_r($autoreply_email, true)
         , "\nmail1: "
         , print_r($mail1, true)
         , "\nmail2: "
         , print_r($mail2, true)
         ,"\n-->";
      */
    }
  }
}

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' => '',
      );
      $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'],
      );
    }
  }

  // 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 = "<a href='{$url}' target='_blank'>{$filename}</a>";
          } 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 "<pre>Form submitted.\n\nData: " . print_r(AccuaForm::getSubmittedData(), true) . '</pre>'; */
    // Get per-form messages (supports multiple forms on same page)
    $messages = AccuaForm::getSubmittedMessages($fid);
    if ($messages && trim($messages) !== '') {
      $out .= '<div id="_response_messages_'.$fid.'" class="accua-form-messages">'.$messages.'</div>';
    }

    // 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 .= '<script>document.addEventListener("DOMContentLoaded",function(){'
          . 'if(history.replaceState)history.replaceState(null,"","#' . $anchor_full . '");'
          . 'var m=document.querySelector(".accua-form-messages");'
          . 'if(m)m.scrollIntoView({behavior:"smooth",block:"start"})'
          . '});</script>';

    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 = '/(<iframe[^>]*>(.*?)<\/iframe>|<script[^>]*>(.*?)<\/script>|<input([^>]*)type="hidden"[^>]*>)/is';
    $out = preg_replace($strip_regexp, '', $out);
  }

  return $out;

}

function accua_forms_include($fid, $atts=array(), $content = '', $code = '') {
  $atts['fid'] = $fid;
  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Shortcode handler manages its own escaping
  echo accua_forms_shortcode_handler($atts, $content, $code);
}

// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Internal helper function, double underscore prefix indicates private
function __accua_forms_submissions_list_page(){
    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only routing, actual actions have nonce checks
    if(isset($_GET['sid'])) {
        accua_forms_single_submission();
      } else {
        accua_forms_submissions_list_page();
      }
}
function accua_forms_submissions_list_page_load(){
    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only routing for screen option registration
    if(isset($_GET['sid'])) {
        // Handle GET-based trash/restore actions here (before any output is sent)
        require_once __DIR__ . '/admin/single-submission.php';
        $sid = (int) $_GET['sid'];
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified below before processing
        if ( $sid && isset( $_GET['action'] ) ) {
            if ( $_GET['action'] === 'trash' ) {
                check_admin_referer( 'del_sub_form_' . $sid );
                accua_forms_trash_submission( $sid );
                wp_safe_redirect( admin_url( 'admin.php?page=accua_forms_submissions_list&trashed=1' ) );
                exit;
            }
            if ( $_GET['action'] === 'restore' ) {
                check_admin_referer( 'restore_sub_form_' . $sid );
                accua_forms_restore_submission( $sid );
                wp_safe_redirect( admin_url( 'admin.php?page=accua_forms_submissions_list&restored=1' ) );
                exit;
            }
        }
        return;
    }
    add_screen_option('per_page', array(
        'default' => 100,
        'option'  => 'accua_forms_submissions_per_page',
    ));

    // Set default hidden columns for first-time users: hide non-essential columns.
    add_filter( 'default_hidden_columns', function( $hidden ) {
        $non_essential = [ 'form_id', 'pid', 'created', 'lead_status' ];
        $avail_fields  = get_option( 'accua_forms_avail_fields', [] );
        foreach ( array_keys( $avail_fields ) as $slug ) {
            if ( $slug !== 'email' ) {
                $non_essential[] = '_field_' . $slug;
            }
        }
        return array_unique( array_merge( $hidden, $non_essential ) );
    } );

    $screen = get_current_screen();
    $screen->add_help_tab( array(
        'id'      => 'accua_forms_lead_statuses',
        'title'   => __( 'Lead Statuses', 'contact-forms' ),
        'content' => '<p>' . accua_forms_get_lead_statuses_help() . '</p>',
    ) );
}
add_filter('set_screen_option_accua_forms_submissions_per_page', function($status, $option, $value) {
    return (int) $value;
}, 10, 3);
function accua_forms_submissions_list_page_head(){
    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only routing, actual actions have nonce checks
    if(isset($_GET['sid'])) {
        require_once __DIR__ . '/admin/single-submission.php';
        accua_forms_single_submission(true);
      } else {
        require_once __DIR__ . '/admin/submissions-list-page.php';
        accua_forms_submissions_list_page(true);
      }

}

/* Generiamo token di sicurezza per poter accedere anche da anonimo - email */
function accua_forms_generate_download_token($subid) {
    global $wpdb;
    $token = wp_generate_password(32, false); // Token casuale di 32 caratteri

    // Controlla se esiste già un token per questo sub_id
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Token lookup requires direct query
    $existing_token = $wpdb->get_var($wpdb->prepare(
        "SELECT afsv_value FROM `{$wpdb->prefix}accua_forms_submissions_values` WHERE afsv_sub_id = %d AND afsv_field_id = '_accua_download_token'",
        $subid
    ));
    if ($existing_token) {
        return $existing_token;
    } else{
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Token insert requires direct query
        $wpdb->insert(
            $wpdb->prefix . 'accua_forms_submissions_values',
            [
                'afsv_sub_id'  => $subid,
                'afsv_field_id' => '_accua_download_token',
                'afsv_type'    => 'token',
                'afsv_value'   => $token
            ],
            ['%d', '%s', '%s', '%s']
        );
        return $token;
    }
}

function accua_forms_check_download_token($subid, $get_token) {
    global $wpdb;
    
    $subid = (int) $subid; // Cast to integer for security
    
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Token verification requires direct query
    $saved_token = $wpdb->get_var($wpdb->prepare(
        "SELECT afsv_value FROM `{$wpdb->prefix}accua_forms_submissions_values` WHERE afsv_sub_id = %d AND afsv_field_id = '_accua_download_token'",
        $subid
    ));
    
    // Debug logging for token verification (comment out in production)
    // error_log("Token check: Submission ID: $subid, Provided token: $get_token, Saved token: $saved_token");
    
    return isset($get_token) && $get_token === $saved_token;
}

/**
 * Gestisce il download di un file inviato tramite un modulo.
 *
 * Questa funzione viene eseguita tramite una richiesta AJAX e permette agli utenti di scaricare
 * un file precedentemente caricato con un modulo. Controlla i parametri della richiesta per verificare
 * la presenza di un file associato a un determinato ID di invio e campo del modulo.
 *
 * - Se il parametro "html" è presente, genera una pagina HTML con un link di reindirizzamento automatico.
 * - Recupera le informazioni del file dal database per verificarne l'esistenza.
 * - Se il file esiste e può essere letto, restituisce il contenuto con gli appropriati header HTTP.
 * - Se il file non viene trovato, restituisce un errore 404.
 *
 * Sicurezza:
 * - Nonce
 * - Utilizza `stripslashes_deep` per sanificare i dati in ingresso.
 * - Protegge il database utilizzando `wpdb->prepare` per prevenire SQL Injection.
 * - Determina il tipo MIME del file per un download sicuro.
 * - Aggiunto token di verifica per utenti
 */

add_action('wp_ajax_accua_forms_download_submitted_file', 'accua_forms_download_submitted_file');
add_action('wp_ajax_nopriv_accua_forms_download_submitted_file', 'accua_forms_download_submitted_file');
function accua_forms_download_submitted_file(){
  $get = stripslashes_deep($_GET);
  $token_valid = false;
  $nonce_valid = false;
  $subid = '';
  
  if(isset($get['subid'])){
    $subid = rawurlencode($get['subid']);
  }
  
  // First verify WordPress nonce for CSRF protection (for logged-in users)
  if (isset($get['_wpnonce']) && wp_verify_nonce($get['_wpnonce'], 'download_file_' . $subid . '_' . $get['field'])) {
    $nonce_valid = true;
  }
  
  // For backward compatibility with older URL format that use 'nonce' instead of '_wpnonce'
  if (!$nonce_valid && isset($get['nonce']) && check_ajax_referer('accua_forms_download_nonce', 'nonce', false)) {
    $nonce_valid = true;
  }
  
  // Check for token-based authentication (for email links and unauthenticated users)
  if (isset($get['token']) && $subid != '') {
    if (accua_forms_check_download_token($subid, $get['token']) == 1) {
      $token_valid = true;
    }
  }
  
  // If both authentication methods fail, deny access
  if (!$nonce_valid && !$token_valid) {
    wp_die(esc_html__('Security check failed.', 'contact-forms'), 403);
  }
  // Additional permission check for admin users
  if(!$token_valid && !$nonce_valid && $subid != ''){ 
      // If neither token nor nonce is valid, check for logged-in admin permissions
      if (!is_user_logged_in() || !current_user_can('manage_options')) {
            wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'contact-forms'));
      }
  }
  if (isset($get['subid'],$get['field'],$get['file'])) {
    if (!empty($get['html'])) { /* export xls*/
      header("Content-type: text/html");
      $subid = rawurlencode($get['subid']);
      $fieldid = rawurlencode($get['field']);
      $filename = rawurlencode($get['file']);
      $url = admin_url('admin-ajax.php') . "?action=accua_forms_download_submitted_file&subid={$subid}&field={$fieldid}&file={$filename}&nonce=" . wp_create_nonce('accua_forms_download_nonce') . "&_wpnonce=" . wp_create_nonce('download_file_' . $subid . '_' . $fieldid);
      if(isset($get['token'])){
          $url .= '&token='.$get['token'];
      }
      $url = esc_url($url);
      $filename = esc_html($get['file']);
      // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $filename and $url are pre-escaped above
      die("<html><head><title>{$filename}</title><meta http-equiv='refresh' content='0;URL={$url}'></head><body><a href='{$url}'>{$filename}</a></body></html>");
    }
    global $wpdb;
    $subid = (int) $get['subid'];
    $field = $get['field'];
    $file = $get['file'];
    $query = $wpdb->prepare(
        "SELECT *
        FROM `{$wpdb->prefix}accua_forms_submissions_values`
        WHERE afsv_sub_id = %d
          AND afsv_field_id = %s
          AND afsv_value = %s",
        $subid,
        $field,
        $file
    );
    // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- $query is prepared above, file download verification requires direct query
    $subval = $wpdb->get_results($query, OBJECT);
    if ($subval) {
      $file_data = get_option('accua_forms_default_file_field_data',array()) + array('dest_path' => '');
      $dest_path = _accua_forms_get_abs_dest_path($file_data['dest_path']);
      $filename = "{$dest_path}/{$subid}_{$field}_{$file}";
      if (is_file($filename) && is_readable($filename)){
        if (function_exists('finfo_open')){
          @ $finfo = finfo_open(FILEINFO_MIME_TYPE);
          if ($finfo) {
            @ $filetype = finfo_file($finfo, $filename);
            @ finfo_close($finfo);
          }
        }
        if (empty($filetype) && function_exists('mime_content_type')){
          @ $filetype = mime_content_type($filename);
        }
        if (empty($filetype)) {
          $filetype = "application/octet-stream";
        }
        // Clean any output buffers to prevent stale content from being sent before the file
        while (ob_get_level()) {
          ob_end_clean();
        }
        // Remove all pre-set headers (admin-ajax.php sets Content-Type: text/html early)
        header_remove();
        nocache_headers();
        header("Content-Type: $filetype");
        header("Content-Length: ".filesize($filename));
        if (empty($_GET['view'])) {
          header("Content-Disposition: attachment; filename=\"$file\"");
        }
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- WP_Filesystem not suitable for binary file streaming
        readfile($filename);
        exit;
      }
    }
  }
  header("HTTP/1.0 404 Not Found");
  //header("Status: 404 Not Found");
  die('File not found');
}

function accua_forms_buildConditionalReplacer($map = array()) {
  if (!class_exists('AccuaConditionalReplacer')){
    require_once('AccuaConditionalReplacer.php');
  }
  return new AccuaConditionalReplacer($map);
}


add_action('wp_ajax_accua_forms_preview', 'accua_forms_preview');
function accua_forms_preview() {
  if (!current_user_can('manage_options')){
    die ('');
  }
  
  // Check nonce for CSRF protection
  $nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
  if ( ! wp_verify_nonce( $nonce, 'accua_forms_preview' ) ) {
    wp_die( esc_html__( 'Security check failed.', 'contact-forms' ), 403 );
  }

  // Enqueue form styles before printing them
  accua_form_enqueue_scripts_and_styles();
  
  // Accept temporary layout override for live preview (before save)
  // This allows real-time preview when user changes layout dropdown
  $preview_layout = '';
  if ( ! empty( $_REQUEST['preview_layout'] ) ) {
    $layout_input   = sanitize_text_field( wp_unslash( $_REQUEST['preview_layout'] ) );
    $allowed_layouts = array( 'toplabel', 'sidebyside', 'inlinelabel' );
    if ( in_array( $layout_input, $allowed_layouts, true ) ) {
      $preview_layout = $layout_input;
    } elseif ( 'default' === $layout_input ) {
      // 'default' means use the global default layout
      $default_form_data = get_option( 'accua_forms_default_form_data', array() );
      $preview_layout = ! empty( $default_form_data['layout'] ) ? $default_form_data['layout'] : 'sidebyside';
    }
  }
  
  // Accept temporary field order for live preview (before save)
  // This allows preview to show reordered fields without saving to database
  $preview_order = null;
  if ( ! empty( $_REQUEST['preview_order'] ) ) {
    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON decoded and validated below
    $order_json = wp_unslash( $_REQUEST['preview_order'] );
    $preview_order = json_decode( $order_json, true );
    if ( json_last_error() !== JSON_ERROR_NONE ) {
      $preview_order = null;
    }
  }
  
  // Store the preview layout override in a filter so shortcode handler can use it
  if ($preview_layout) {
    add_filter('accua_forms_preview_layout_override', function() use ($preview_layout) {
      return $preview_layout;
    });
  }
  
  // Store the preview order override in a filter so shortcode handler can use it
  if ($preview_order) {
    add_filter('accua_forms_preview_order_override', function() use ($preview_order) {
      return $preview_order;
    });
  }
  
  // Signal that we're in admin preview mode - form generator should read from draft
  add_filter('accua_forms_use_draft_for_preview', '__return_true');
  
  echo '<html><head>
  <style>
    *, *::before, *::after { box-sizing: border-box; }
    body {
      font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
      margin: 0;
      padding: 16px;
      background: #fff;
      font-size: 14px;
      line-height: 1.5;
    }
  </style>';
  wp_print_styles();
  wp_print_head_scripts();
  echo '</head><body>';
  $preview_fid = isset($_REQUEST['fid']) ? sanitize_text_field(wp_unslash($_REQUEST['fid'])) : '';

  // Check if the form has any fields — show placeholder if empty
  $draft_data = _accua_forms_get_draft_data($preview_fid);
  if (empty($draft_data['fields'])) {
    echo '<p style="color:#50575e;text-align:center;margin-top:40px;">' . esc_html__('Add fields to the form to see the preview.', 'contact-forms') . '</p>';
  } else {
    // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Shortcode handler manages its own escaping
    echo accua_forms_shortcode_handler(array('fid' => $preview_fid));
  }
  wp_print_footer_scripts();
  echo '</body></html>';
  die('');
}

//salvataggio file excel
add_action('wp_ajax_accua_forms_submission_page_save_excel', 'accua_forms_submission_page_save_excel');
//add_action('wp_ajax_nopriv_accua_forms_submission_page_save_excel', 'accua_forms_submission_page_save_excel');

function accua_forms_submission_page_save_excel() {
  if (!current_user_can('manage_options')){
    header("HTTP/1.0 401 Access Denied");
    //header("Status: 401 Access Denied");
    die('You are not authorized to access this page.');
  }
  
  // Check nonce for CSRF protection
  if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])), 'accua_forms_export_excel')) {
    wp_die(esc_html__('Security check failed.', 'contact-forms'), 403);
  }

  require_once __DIR__ . '/admin/submissions-list-page.php';
  $listTable = new Accua_Forms_Submissions_List_Table();
  $listTable->export_xls = true;
  $listTable->prepare_items(true);
  // Sanitize column selection input
  $show_col_input = isset($_GET['accua_show_field']) ? sanitize_text_field(wp_unslash($_GET['accua_show_field'])) : '';
  $show_col = array_map('sanitize_key', explode(',', $show_col_input));
  $show_col = array_diff($show_col, array('singlesub'));
  header('Content-disposition: attachment; filename=downloads-report.xls');
  header('Content-type: application/vnd.ms-excel');
  accua_forms_submission_page_save_excel_general($listTable,$show_col);
  die('');
}



function accua_forms_submission_page_save_excel_general(Accua_Forms_Submissions_List_Table $listTable,array $show_col,array $options=array()) {
  global $wpdb;

  $content_type = 'text/html; charset=' . get_option('blog_charset');

  //creo il file excel
  ?><html xmlns:o="urn:schemas-microsoft-com:office:office"
  xmlns:x="urn:schemas-microsoft-com:office:excel"
  xmlns="http://www.w3.org/TR/REC-html40">
  <head>
  <meta http-equiv=Content-Type content="<?php echo esc_attr( $content_type ); ?>" />
  <meta name=ProgId content=Excel.Sheet />
  <style>
  <!--
  td {vertical-align:top;}
  .head_row {font-weight:bold;}
  .column-date { mso-number-format:"Short Date"; }
  .datetime_cell { mso-number-format:"yyyy\\-mm\\-dd\\ hh\:mm\:ss"; }
  -->
  </style>
  <!--[if gte mso 9]><xml>
  <x:ExcelWorkbook>
  <x:ExcelWorksheets>
  <x:ExcelWorksheet>
  <x:WorksheetOptions>
  <x:FreezePanes/>
  <x:FilterOn/>
  <x:SplitHorizontal>1</x:SplitHorizontal>
  <x:TopRowBottomPane>1</x:TopRowBottomPane>
  <x:ActivePane>2</x:ActivePane>
  <x:Panes>
  <x:Pane>
  <x:Number>3</x:Number>
  </x:Pane>
  <x:Pane>
  <x:Number>2</x:Number>
  </x:Pane>
  </x:Panes>
  </x:WorksheetOptions>
  </x:ExcelWorksheet>
  </x:ExcelWorksheets>
  </x:ExcelWorkbook>
  </xml><![endif]-->
  </head>
  <body>
  <table x:str border=1 >
  <tr class='head-row'>
  <?php
  $cols = $listTable->get_columns();
  foreach($cols as $col_key=>$col_value) {
    if(in_array($col_key, $show_col)) { ?>
      <td x:autofilter="all"><?php echo esc_html( $col_value ); ?></td>
    <?php }
  } ?>
  </tr>

  <?php
  $lead_statuses = accua_forms_get_lead_statuses();

  foreach($listTable->items as $id_submission=>$single_submission) {
     // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required to prevent timeout during large exports
     @ set_time_limit(10);
     echo "<tr>";
     foreach($cols as $col_key=>$col_value) {
      if(in_array($col_key, $show_col)) {
        echo '<td class="' . esc_attr($col_key) . '">';
        if ($col_key == 'lead_status') {
          if (isset($lead_statuses[$single_submission['lead_status']])) {
            echo esc_html($lead_statuses[$single_submission['lead_status']]);
          }
        } elseif(isset($single_submission[$col_key])) {
              if ( method_exists( $listTable, 'column_' . $col_key ) ) {
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- List table column methods handle their own escaping
                echo call_user_func( array( &$listTable, 'column_' . $col_key ), $single_submission );
              } else {
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- List table column_default handles escaping
                echo $listTable->column_default( $single_submission, $col_key );
              }
         }
         echo "</td>";
      }

    }
   echo "</tr>";
  }

  ?>
  </table>
  </body>
  </html>
  <?php
}

function accua_forms_print_tokens() {
  $avail_fields = get_option('accua_forms_avail_fields', array());
  $tokens = '';
  foreach($avail_fields as $key=>$value) {
    $field_name = $value['name'] ?? $value['label'] ?? $key;
    $tokens .= $field_name . ": {" . $key . "}\n";
    switch ($value['type']) {
      case 'file':
        $tokens .= $field_name . " (download link): {__download_" . $key . "}\n";
      break;
      case 'multiselect':
      case 'multicheckbox':
        $tokens .= $field_name . " (labels): {__label_" . $key . "}\n";
      break;
      case 'select':
      case 'radio':
        $tokens .= $field_name . " (label): {__label_" . $key . "}\n";
      break;
      case 'post-multicheckbox':
        $tokens .= $field_name . " (posts titles): {__label_" . $key . "}\n";
        $tokens .= $field_name . " (posts ids): {__post_id_" . $key . "}\n";
        $tokens .= $field_name . " (posts urls): {__post_url_" . $key . "}\n";
      break;
      case 'post-select':
        $tokens .= $field_name . " (post title): {__label_" . $key . "}\n";
        $tokens .= $field_name . " (post id): {__post_id_" . $key . "}\n";
        $tokens .= $field_name . " (post url): {__post_url_" . $key . "}\n";
      break;
    }
  }

  // phpcs:disable PluginCheck.CodeAnalysis.Heredoc.NotAllowed, WordPress.Security.EscapeOutput.HeredocOutputNotEscaped -- Heredoc for tokens help HTML
  echo <<<EOT
<div class="accua_forms_token_list">
<h2>Tokens</h2>
<em>In HTML text, use {!token_name} to insert unfiltered token value</em>
<h3>Fields</h3>
<em>These tokens are available only if the field is added to the form</em>
<pre>$tokens</pre>
<h3>Generic tokens</h3>
<pre>{__fid}
{__subid}
{__pid}
{__ip}
{__anonymized_ip}
{__uri}
{__url}
{__referrer}
{__lang}
{__locale}
{__created}
{__created_day}
{__created_day_month_year}
{__created_hour}
{__submitted}
{__submitted_day}
{__submitted_day_month_year}
{__submitted_hour}
{__user_agent}
{__platform}
{__tentatives}
{__submit_method}
{__submitted_txt}
{__submitted_html}
{__submitted_json}
{__autoreply}
{__autoreply_email}
{__confirmation_emails_message}
{__review_submission_url}</pre>
</div>
EOT;
  // phpcs:enable PluginCheck.CodeAnalysis.Heredoc.NotAllowed, WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
  do_action('accua_forms_print_tokens');
}

/**
 * Get posts/pages for post-select fields using get_posts() for WPML compatibility.
 *
 * Uses WordPress get_posts() instead of direct SQL to ensure WPML and other
 * language plugins can filter results to current language automatically.
 *
 * Performance considerations:
 * - Results are cached using transients (5 minute TTL) to reduce database queries
 * - meta_key/meta_value queries are necessary for filtering by custom fields
 * - post__not_in is used only when exclude is explicitly requested by admin
 * - Default limit of 500 posts prevents runaway queries
 *
 * Hierarchy handling:
 * - child_of returns all descendants of the given post; exclude_tree removes a
 *   post and all its descendants. Both are resolved to explicit ID lists via
 *   accua_forms_get_post_descendant_ids() before querying, so they remain
 *   correct with pagination and search.
 * - hierarchical only affects ordering (parents before children) and only when
 *   the result set is complete and title-sorted; it never drops posts whose
 *   parent is unavailable (e.g. published children of draft parents).
 *
 * @since 2.0.0-beta.29
 * @param string|array $args Query arguments (backward compatible with old function).
 * @return array Array of post objects.
 */
function accua_get_pages($args = '') {
  // phpcs:disable WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude, WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- These are function parameter defaults, not actual query execution.
  $defaults = array(
    'child_of'          => 0,
    'sort_order'        => 'ASC',
    'sort_column'       => 'post_title',
    'hierarchical'      => 1,
    'exclude'           => array(),
    'include'           => array(),
    'meta_key'          => '',
    'meta_value'        => '',
    'meta_value_lt'     => '',
    'meta_value_gt'     => '',
    'meta_value_le'     => '',
    'meta_value_ge'     => '',
    'meta_value_like'   => '',
    'meta_value_format' => 'string',
    'authors'           => '',
    'parent'            => -1,
    'exclude_tree'      => '',
    'number'            => 500, // Default limit for performance
    'offset'            => 0,
    'post_type'         => 'page',
    'post_status'       => 'publish',
    'suppress_filters'  => false, // IMPORTANT: Allow WPML to filter by language
    's'                 => '',    // Search term (new parameter for AJAX search)
  );
  // phpcs:enable WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude, WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value

  $r = wp_parse_args($args, $defaults);

  // Generate cache key based on arguments and current language
  $cache_key_data = $r;
  // Add current language to cache key for WPML/Polylang compatibility
  if (function_exists('pll_current_language')) {
    $cache_key_data['_lang'] = pll_current_language();
  } elseif (defined('ICL_LANGUAGE_CODE')) {
    $cache_key_data['_lang'] = ICL_LANGUAGE_CODE;
  }
  $cache_key = 'accua_pages_' . md5(wp_json_encode($cache_key_data));

  // Check transient cache first (skip for search queries and random ordering)
  $use_cache = empty($r['s']) && $r['sort_column'] !== 'rand';
  if ($use_cache) {
    $cached = get_transient($cache_key);
    if ($cached !== false) {
      return $cached;
    }
  }

  // Validate post type
  $post_type = $r['post_type'];
  if (!is_array($post_type)) {
    $post_type = array_map('trim', explode(',', $post_type));
  }
  $valid_post_types = get_post_types();
  $post_type = array_filter($post_type, function($pt) use ($valid_post_types) {
    return in_array($pt, $valid_post_types, true);
  });
  if (empty($post_type)) {
    return array();
  }

  // Validate post status
  $post_status = $r['post_status'];
  if (!is_array($post_status)) {
    $post_status = array_map('trim', explode(',', $post_status));
  }
  $valid_statuses = get_post_stati();
  $post_status = array_filter($post_status, function($ps) use ($valid_statuses) {
    return in_array($ps, $valid_statuses, true);
  });
  if (empty($post_status)) {
    $post_status = array('publish');
  }

  // Map sort_column to orderby
  $orderby_map = array(
    'post_title'    => 'title',
    'title'         => 'title',
    'post_date'     => 'date',
    'date'          => 'date',
    'post_modified' => 'modified',
    'modified'      => 'modified',
    'menu_order'    => 'menu_order',
    'post_name'     => 'name',
    'name'          => 'name',
    'post_parent'   => 'parent',
    'parent'        => 'parent',
    'ID'            => 'ID',
    'rand'          => 'rand',
    'comment_count' => 'comment_count',
    'post_author'   => 'author',
    'author'        => 'author',
  );
  $sort_column = $r['sort_column'];
  $orderby = isset($orderby_map[$sort_column]) ? $orderby_map[$sort_column] : 'title';

  // Build get_posts arguments
  $query_args = array(
    'post_type'        => $post_type,
    'post_status'      => $post_status,
    'orderby'          => $orderby,
    'order'            => strtoupper($r['sort_order']) === 'DESC' ? 'DESC' : 'ASC',
    'posts_per_page'   => !empty($r['number']) ? (int) $r['number'] : 500,
    'offset'           => (int) $r['offset'],
    'suppress_filters' => (bool) $r['suppress_filters'],
  );

  // Search term
  if (!empty($r['s'])) {
    $query_args['s'] = sanitize_text_field($r['s']);
  }

  // Include specific posts (overrides other filters)
  if (!empty($r['include'])) {
    $include = wp_parse_id_list($r['include']);
    if (!empty($include)) {
      $query_args['post__in'] = $include;
      $query_args['orderby'] = 'post__in'; // Preserve include order
    }
  } else {
    // Exclude posts - only used when admin explicitly configures exclusions.
    // exclude_tree also removes all descendants of the given post, resolved
    // against the full tree so it works with pagination and search.
    $exclude_ids = array();
    if (!empty($r['exclude'])) {
      $exclude_ids = wp_parse_id_list($r['exclude']);
    }
    if (!empty($r['exclude_tree'])) {
      $exclude_tree = (int) $r['exclude_tree'];
      $exclude_ids = array_merge($exclude_ids, array($exclude_tree), accua_forms_get_post_descendant_ids($exclude_tree, $post_type));
    }

    // Child of: restrict to all descendants of the given post, like core get_pages().
    // Resolved to an explicit ID list so it stays correct with pagination and search.
    if (!empty($r['child_of'])) {
      $descendant_ids = accua_forms_get_post_descendant_ids((int) $r['child_of'], $post_type);
      // post__in cannot be combined with post__not_in, so exclusions are applied to the list itself.
      $descendant_ids = array_values(array_diff($descendant_ids, $exclude_ids));
      $query_args['post__in'] = !empty($descendant_ids) ? $descendant_ids : array(0);
    } elseif (!empty($exclude_ids)) {
      // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Exclusion is an optional admin-configured feature, not default behavior.
      $query_args['post__not_in'] = $exclude_ids;
    }

    // Parent filter (direct children only)
    if ((int) $r['parent'] >= 0) {
      $query_args['post_parent'] = (int) $r['parent'];
    }

    // Authors filter
    if (!empty($r['authors'])) {
      $author_ids = array();
      $post_authors = preg_split('/[\s,]+/', $r['authors']);
      foreach ($post_authors as $post_author) {
        $post_author = trim($post_author);
        if (empty($post_author)) {
          continue;
        }
        if (is_numeric($post_author)) {
          $author_ids[] = (int) $post_author;
        } else {
          $user = get_user_by('login', $post_author);
          if ($user && !empty($user->ID)) {
            $author_ids[] = $user->ID;
          }
        }
      }
      if (!empty($author_ids)) {
        $query_args['author__in'] = $author_ids;
      }
    }

    // Build meta_query for advanced meta comparisons
    $meta_query = array();

    // Standard meta_key/meta_value - used for filtering posts by custom field.
    // This is an optional admin-configured feature for advanced post filtering.
    if (!empty($r['meta_key'])) {
      // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Required for custom field filtering feature.
      $query_args['meta_key'] = stripslashes($r['meta_key']);
      if (!empty($r['meta_value'])) {
        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Required for custom field filtering feature.
        $query_args['meta_value'] = stripslashes($r['meta_value']);
      }
    }

    // Advanced meta comparisons (lt, gt, le, ge, like)
    if (!empty($r['meta_key']) && (
      !empty($r['meta_value_lt']) || !empty($r['meta_value_gt']) ||
      !empty($r['meta_value_le']) || !empty($r['meta_value_ge']) ||
      !empty($r['meta_value_like'])
    )) {
      $meta_key = stripslashes($r['meta_key']);
      $meta_type = 'CHAR';
      switch ($r['meta_value_format']) {
        case 'int':
          $meta_type = 'NUMERIC';
          break;
        case 'float':
          $meta_type = 'DECIMAL';
          break;
        case 'timestamp':
          $meta_type = 'DATETIME';
          break;
      }

      if (!empty($r['meta_value_lt'])) {
        $value = stripslashes($r['meta_value_lt']);
        if ($r['meta_value_format'] === 'timestamp') {
          $value = gmdate('Y-m-d H:i:s', strtotime($value));
        }
        $meta_query[] = array(
          'key'     => $meta_key,
          'value'   => $value,
          'compare' => '<',
          'type'    => $meta_type,
        );
      }
      if (!empty($r['meta_value_gt'])) {
        $value = stripslashes($r['meta_value_gt']);
        if ($r['meta_value_format'] === 'timestamp') {
          $value = gmdate('Y-m-d H:i:s', strtotime($value));
        }
        $meta_query[] = array(
          'key'     => $meta_key,
          'value'   => $value,
          'compare' => '>',
          'type'    => $meta_type,
        );
      }
      if (!empty($r['meta_value_le'])) {
        $value = stripslashes($r['meta_value_le']);
        if ($r['meta_value_format'] === 'timestamp') {
          $value = gmdate('Y-m-d H:i:s', strtotime($value));
        }
        $meta_query[] = array(
          'key'     => $meta_key,
          'value'   => $value,
          'compare' => '<=',
          'type'    => $meta_type,
        );
      }
      if (!empty($r['meta_value_ge'])) {
        $value = stripslashes($r['meta_value_ge']);
        if ($r['meta_value_format'] === 'timestamp') {
          $value = gmdate('Y-m-d H:i:s', strtotime($value));
        }
        $meta_query[] = array(
          'key'     => $meta_key,
          'value'   => $value,
          'compare' => '>=',
          'type'    => $meta_type,
        );
      }
      if (!empty($r['meta_value_like'])) {
        $meta_query[] = array(
          'key'     => $meta_key,
          'value'   => stripslashes($r['meta_value_like']),
          'compare' => 'LIKE',
        );
      }

      if (!empty($meta_query)) {
        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Required for advanced meta comparison operators (lt, gt, like, etc.).
        $query_args['meta_query'] = $meta_query;
        // Remove simple meta_value if we're using meta_query
        unset($query_args['meta_value']);
      }
    }
  }

  // Get posts using WordPress function (WPML automatically filters by current language)
  $pages = get_posts($query_args);

  if (empty($pages)) {
    // Cache empty results too (5 minutes)
    if ($use_cache) {
      set_transient($cache_key, array(), 5 * MINUTE_IN_SECONDS);
    }
    /**
     * Filters the list of pages retrieved from accua_get_pages.
     *
     * @since 2.0.0-beta.29
     *
     * @param array $pages List of page objects.
     * @param array $r     Arguments passed to accua_get_pages.
     */
    return apply_filters('accua_forms_get_pages', array(), $r);
  }

  // Hierarchical (tree) ordering: list parents before their children, like core
  // get_pages(). Only applied when the result set is complete (no search, no
  // offset, not truncated by the limit) and title-sorted — reordering a paginated
  // or filtered slice would drop children whose parent is not in the same slice.
  if ($r['hierarchical'] && empty($r['s']) && (int) $r['offset'] === 0
      && count($pages) < (int) $query_args['posts_per_page']
      && $orderby === 'title'
      && function_exists('get_page_children')) {
    $tree_ordered = get_page_children((int) $r['child_of'], $pages);
    if (count($tree_ordered) < count($pages)) {
      // Posts whose ancestors are not part of the result set (e.g. published
      // children of a draft parent) go at the end instead of being dropped.
      $tree_ids = array();
      foreach ($tree_ordered as $page) {
        $tree_ids[$page->ID] = true;
      }
      foreach ($pages as $page) {
        if (!isset($tree_ids[$page->ID])) {
          $tree_ordered[] = $page;
        }
      }
    }
    $pages = $tree_ordered;
  }

  // Cache results for 5 minutes to improve performance
  if ($use_cache) {
    set_transient($cache_key, $pages, 5 * MINUTE_IN_SECONDS);
  }

  /** This filter is documented above */
  return apply_filters('accua_forms_get_pages', $pages, $r);
}

/**
 * Get the IDs of all descendants of a post by traversing the post_parent tree.
 *
 * Used to resolve the child_of and exclude_tree arguments of accua_get_pages()
 * to an explicit ID list, so the main query stays correct with pagination and
 * search. Traverses posts of any status so that e.g. a published grandchild of
 * a draft child is still found (the main query applies its own status filter).
 *
 * @since 2.2.27
 * @param int          $parent_id Root post ID (not included in the result).
 * @param string|array $post_type Post type(s) to traverse.
 * @return int[] Descendant post IDs.
 */
function accua_forms_get_post_descendant_ids($parent_id, $post_type) {
  $descendant_ids = array();
  $level = array((int) $parent_id);
  // Depth guard: hierarchies deeper than 25 levels are treated as data corruption (parent loops).
  for ($depth = 0; $depth < 25 && !empty($level); $depth++) {
    $children = get_posts(array(
      'post_type'        => $post_type,
      'post_status'      => 'any',
      'post_parent__in'  => $level,
      'posts_per_page'   => -1,
      'fields'           => 'ids',
      'suppress_filters' => true, // Structural traversal: do not let language plugins hide ancestors.
      'orderby'          => 'ID',
      'order'            => 'ASC',
    ));
    $children = array_map('intval', array_diff($children, $descendant_ids, array((int) $parent_id)));
    $descendant_ids = array_merge($descendant_ids, $children);
    $level = $children;
  }
  return $descendant_ids;
}

// phpcs:disable WordPress.DB.DirectDatabaseQuery
function accua_forms_trash_submission($id_sub){
  global $wpdb;
  return $wpdb->query($wpdb->prepare("UPDATE `{$wpdb->prefix}accua_forms_submissions` SET afs_status = -1 WHERE afs_id = %d", $id_sub)) !== FALSE;
}

function accua_forms_restore_submission($id_sub){
  global $wpdb;
  return $wpdb->query($wpdb->prepare("UPDATE `{$wpdb->prefix}accua_forms_submissions` SET afs_status = 0 WHERE afs_id = %d", $id_sub)) !== FALSE;
}
// phpcs:enable WordPress.DB.DirectDatabaseQuery

/**
 * Clear accua_get_pages cache when posts are modified.
 *
 * Called when posts are created, updated, deleted, or have status changed.
 * This ensures that post-select dropdowns always show fresh data.
 *
 * @since 2.0.0-beta.29
 * @param int $post_id Post ID that was modified.
 */
function accua_forms_clear_pages_cache($post_id = 0) {
  global $wpdb;
  // Delete all transients that start with 'accua_pages_'
  // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct query required to delete transients by prefix, and we're clearing cache not reading data.
  $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_accua_pages_%' OR option_name LIKE '_transient_timeout_accua_pages_%'");
}
// Clear cache when posts are modified
add_action('save_post', 'accua_forms_clear_pages_cache');
add_action('delete_post', 'accua_forms_clear_pages_cache');
add_action('trash_post', 'accua_forms_clear_pages_cache');
add_action('untrash_post', 'accua_forms_clear_pages_cache');

function accua_forms_get_lead_statuses() {
  static $statuses = NULL;
  if ($statuses === NULL) {
    $statuses = array(
      0 => __('Undefined', 'contact-forms'),
      -1 => __('Spam', 'contact-forms'),
      1 => __('Job Candidate', 'contact-forms'),
      2 => __('Lead', 'contact-forms'),
      3 => __('Prospect', 'contact-forms'),
      4 => __('Opportunity', 'contact-forms'),
      5 => __('Customer', 'contact-forms'),
      6 => __('Supplier', 'contact-forms'),
      7 => __('Other', 'contact-forms'),
    );
  }
  return $statuses;
}

/**
 * Get lead statuses help text (used in toggletip and Help Tab).
 */
function accua_forms_get_lead_statuses_help() {
  return '<strong>' . esc_html__( 'Spam', 'contact-forms' ) . '</strong> – ' . esc_html__( 'All submissions that can be discarded immediately, including submission tests', 'contact-forms' ) . '<br>'
    . '<strong>' . esc_html__( 'Job Candidate', 'contact-forms' ) . '</strong> – ' . esc_html__( 'Includes spontaneous and specific job applications', 'contact-forms' ) . '<br>'
    . '<strong>' . esc_html__( 'Lead', 'contact-forms' ) . '</strong> – ' . esc_html__( 'Unclear (general info request)', 'contact-forms' ) . '<br>'
    . '<strong>' . esc_html__( 'Prospect', 'contact-forms' ) . '</strong> – ' . esc_html__( 'A qualified lead passed to Sales', 'contact-forms' ) . '<br>'
    . '<strong>' . esc_html__( 'Opportunity', 'contact-forms' ) . '</strong> – ' . esc_html__( 'Quote / Pricing request that must be followed up', 'contact-forms' ) . '<br>'
    . '<strong>' . esc_html__( 'Customer', 'contact-forms' ) . '</strong> – ' . esc_html__( 'Has already purchased', 'contact-forms' ) . '<br>'
    . '<strong>' . esc_html__( 'Supplier', 'contact-forms' ) . '</strong> – ' . esc_html__( 'Contact whose role is or can only be supplier of goods and services', 'contact-forms' ) . '<br>'
    . '<strong>' . esc_html__( 'Other', 'contact-forms' ) . '</strong> – ' . esc_html__( 'Contact is valid but not within lead generation', 'contact-forms' );
}

function accua_forms_select_lead_status($subid, $original_lead_status) {
  $subid = absint( $subid );
  $original_lead_status = absint( $original_lead_status );
  $nonce = esc_attr( wp_json_encode( wp_create_nonce( "set_lead_status_$subid" ) ) );
  $ret = '<select onchange="accua_forms_set_lead_status(this, ' . $subid . ', ' . $nonce . ', ' . $original_lead_status . ')">';
  $statuses = accua_forms_get_lead_statuses();
  foreach ($statuses as $k => $l) {
    $selected = ( (int) $k === $original_lead_status ) ? ' selected="selected" ' : '';
    $ret .= '<option value="' . esc_attr( $k ) . '"' . $selected . '>' . esc_html( $l ) . '</option>';
  }
  $ret .= '</select>';
  return $ret;
}

add_action( 'wp_ajax_accua-forms-set-lead-status' , 'accua_forms_set_lead_status');
function accua_forms_set_lead_status() {
  if (!current_user_can('manage_options')){
    wp_die(0, 403);
  }
  // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verification happens after subid is extracted via check_ajax_referer()
  $post = $_POST + array(
    'subid' => 0,
    'lead_status' => 0,
  );
  $subid = (int) $post['subid'];

  if ($subid) {
    check_ajax_referer("set_lead_status_$subid", '_nonce_set_lead_status');
    $lead_status = (int) $post['lead_status'];
    $statuses = accua_forms_get_lead_statuses();
    if (isset($statuses[$lead_status])) {
      global $wpdb;
      // phpcs:disable WordPress.DB.DirectDatabaseQuery
      $ret = $wpdb->update(
        "{$wpdb->prefix}accua_forms_submissions",
        array('afs_lead_status' => $lead_status),
        array('afs_id' => $subid),
        array('%d'),
        array('%d')
      );
      // phpcs:enable WordPress.DB.DirectDatabaseQuery
      if ($ret !== FALSE) {
        wp_die(1);
      }
    }
  }
  wp_die(0, 500);
}

add_action( 'wp_ajax_accua-forms-add-note', 'accua_forms_ajax_add_note' );
function accua_forms_ajax_add_note() {
  $sub_id = isset( $_POST['subid'] ) ? (int) $_POST['subid'] : 0;
  $text   = isset( $_POST['text'] ) ? sanitize_textarea_field( wp_unslash( $_POST['text'] ) ) : '';

  if ( ! $sub_id || ! $text ) {
    wp_send_json_error();
  }
  check_ajax_referer( "submission_{$sub_id}_note_add", '_nonce' );
  if ( ! current_user_can( 'manage_options' ) ) {
    wp_send_json_error();
  }

  require_once __DIR__ . '/admin/single-submission.php';
  $result = accua_forms_add_submission_note( $sub_id, $text );
  if ( ! $result ) {
    wp_send_json_error();
  }
  $result['del_nonce'] = wp_create_nonce( "submission_{$sub_id}_note_del" );
  wp_send_json_success( $result );
}

add_action( 'wp_ajax_accua-forms-delete-note', 'accua_forms_ajax_delete_note' );
function accua_forms_ajax_delete_note() {
  $sub_id = isset( $_POST['subid'] ) ? (int) $_POST['subid'] : 0;
  $date   = isset( $_POST['date'] ) ? sanitize_text_field( wp_unslash( $_POST['date'] ) ) : '';

  if ( ! $sub_id || ! $date ) {
    wp_send_json_error();
  }
  check_ajax_referer( "submission_{$sub_id}_note_del", '_nonce' );
  if ( ! current_user_can( 'manage_options' ) ) {
    wp_send_json_error();
  }

  require_once __DIR__ . '/admin/single-submission.php';
  if ( ! accua_forms_delete_submission_note( $sub_id, $date ) ) {
    wp_send_json_error();
  }
  wp_send_json_success();
}
```
