message = ''; parent::__construct( array( 'singular' => 'submission', 'plural' => 'submissions', 'ajax' => false ) ); } function get_num_of_active_items () { return $this->active_items; } function get_num_of_del_items () { return $this->del_items; } function get_items_per_lead_status() { return $this->items_per_lead_status; } function column_default($item, $column_name){ if (isset($item[$column_name])) { return $item[$column_name]; } else { return ''; } } function column_cb($item){ return sprintf( '', /*$1%s*/ $this->_args['singular'], /*$2%s*/ $item['ID'] ); } function column_lead_status($item) { return accua_forms_select_lead_status($item['ID'], $item['lead_status']); } function column_ID($item) { $view_url = admin_url('admin.php?page=accua_forms_submissions_list&sid=' . $item['ID']); $title = sprintf('%s', esc_url($view_url), esc_html($item['ID'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only view toggle, not a state-changing action $del = isset($_GET['del']) && $_GET['del'] == 1; $actions = array( 'view' => sprintf('%s', esc_url($view_url), esc_html__('View', 'contact-forms')), ); if ($del) { $restore_url = wp_nonce_url(admin_url('admin.php?page=accua_forms_submissions_list&del=1&action=restore&submission[]=' . $item['ID']), 'bulk-submissions'); $actions['restore'] = sprintf('%s', esc_url($restore_url), esc_html__('Restore', 'contact-forms')); $shred_url = wp_nonce_url(admin_url('admin.php?page=accua_forms_submissions_list&del=1&action=shred&submission[]=' . $item['ID']), 'bulk-submissions'); $actions['shred'] = sprintf('%s', esc_url($shred_url), esc_js(__('Are you sure you want to permanently delete this submission? This action cannot be undone.', 'contact-forms')), esc_html__('Permanently delete', 'contact-forms')); } else { $trash_url = wp_nonce_url(admin_url('admin.php?page=accua_forms_submissions_list&action=delete&submission[]=' . $item['ID']), 'bulk-submissions'); $actions['trash'] = sprintf('%s', esc_url($trash_url), esc_html__('Move to trash', 'contact-forms')); } return $title . $this->row_actions($actions); } function column_singlesub($item){ return ''. __('Open', 'contact-forms').''; } function column_uri( $item ) { $value = $item['uri'] ?? ''; $url = $value !== '' ? home_url( $value ) : ''; return $this->truncate_long_value( $value, 80, $url ); } function column_referrer( $item ) { $value = $item['referrer'] ?? ''; return $this->truncate_long_value( $value, 80, $value ); } /** * Render a potentially long value with a native
toggle when it exceeds 80 chars. * When $url is provided, the expanded text becomes a clickable link. */ private function truncate_long_value( $value, $max = 80, $url = '' ) { if ( $value === '' ) { return ''; } if ( $url !== '' ) { // JS handles overflow detection and the expandable widget for URL columns. return '' . esc_html( $value ) . ''; } // Plain text (no URL): keep PHP-side truncation. if ( mb_strlen( $value ) <= $max ) { return esc_html( $value ); } $truncated = mb_substr( $value, 0, $max ) . "\u{2026}"; return '
' . '' . esc_html( $truncated ) . '' . esc_html( $value ) . '
'; } protected function get_primary_column_name() { return 'ID'; } function no_items() { esc_html_e('No submissions found.', 'contact-forms'); } function set_message($single_message) { $this->message=$single_message; } function get_message() { if($this->message!=NULL) return $this->message; else return NULL; } function get_columns(){ global $wpdb; $columns = array( 'cb' => '', //Render a checkbox instead of text 'ID' => 'ID', 'form_title' => 'Form', 'form_id' => __("Form ID", 'contact-forms'), 'pid' => __("Page ID", 'contact-forms'), 'ip' => 'IP', 'uri' => __("Page", 'contact-forms'), 'referrer' => __("Referrer", 'contact-forms'), 'lang' => __("Language", 'contact-forms'), 'created' => __("Opened", 'contact-forms'), 'submitted' => __("Submitted", 'contact-forms'), ); $query = "SELECT DISTINCT afsv_field_id FROM `{$wpdb->prefix}accua_forms_submissions_values`"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- No user input $res = $wpdb->get_col($query); $avail_fields = get_option('accua_forms_avail_fields', array()); foreach ($res as $col) { if (empty($avail_fields[$col]) || !isset($avail_fields[$col]['name'])) { /* translators: %s: field slug/identifier */ $columns['_field_'.$col] = sprintf( __( '%s (removed)', 'contact-forms' ), $col ); $this->removed_columns['_field_'.$col] = $col; } else { $columns['_field_'.$col] = $avail_fields[$col]['name']; } } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only view toggle, not a state-changing action if(isset($_GET['del']) && $_GET['del']==1){ } else{ $columns['lead_status'] = __('Lead Status', 'contact-forms'); $columns['singlesub'] = __('Open', 'contact-forms'); } return $columns; } function get_bulk_actions() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only view toggle, not a state-changing action if(isset($_GET['del']) && $_GET['del']==1) { $actions = array( 'restore' => __('Restore', 'contact-forms'), 'shred' => __('Permanently delete', 'contact-forms'), ); } else { $actions = array( 'delete' => __('Move to trash', 'contact-forms'), 'anonymize' => __('Anonymize', 'contact-forms'), ); } return $actions; } protected function get_views() { $views = array(); $base_url = admin_url('admin.php?page=accua_forms_submissions_list'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only view routing $del = isset($_GET['del']) && $_GET['del'] == 1; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only view routing $active_lead_status = isset($_GET['lead_status']) ? (int) $_GET['lead_status'] : null; $class = (!$del && $active_lead_status === null) ? 'current' : ''; $views['active'] = sprintf( '%s (%s)', esc_url($base_url), $class, esc_html__('Active', 'contact-forms'), number_format_i18n($this->active_items) ); $class = $del ? 'current' : ''; $views['trash'] = sprintf( '%s (%s)', esc_url(add_query_arg('del', '1', $base_url)), $class, esc_html__('Trash', 'contact-forms'), number_format_i18n($this->del_items) ); $lead_statuses = accua_forms_get_lead_statuses(); foreach ($lead_statuses as $lead_status_id => $lead_status_label) { if (!empty($this->items_per_lead_status[$lead_status_id]->n)) { $id = absint($lead_status_id); $class = ($active_lead_status === $id) ? 'current' : ''; $views['lead_' . $id] = sprintf( '%s (%s)', esc_url(add_query_arg('lead_status', $id, $base_url)), $class, esc_attr__('Lead status', 'contact-forms'), esc_html($lead_status_label), number_format_i18n(absint($this->items_per_lead_status[$lead_status_id]->n)) ); } } return $views; } function prepare_items($all=false, $get = array()) { global $wpdb, $hook_suffix; if (!$get) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Admin list table uses WordPress nonce verification via check_admin_referer $get = stripslashes_deep($_GET); } $per_page = $this->get_items_per_page('accua_forms_submissions_per_page', 100); $del = isset($get['del']) && $get['del']==1; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- No user input $this->active_items = $wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}accua_forms_submissions` WHERE afs_status >= 0"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- No user input $this->del_items = $wpdb->get_var("SELECT COUNT(*) FROM `{$wpdb->prefix}accua_forms_submissions` WHERE afs_status < 0"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- No user input $this->items_per_lead_status = $wpdb->get_results("SELECT afs_lead_status AS status, COUNT(*) AS n FROM `{$wpdb->prefix}accua_forms_submissions` WHERE afs_status >= 0 GROUP BY afs_lead_status", OBJECT_K); $filter = $filter_query_custom_field = ""; $search = ''; if(isset($get['fid']) && ($get['fid'])!=-1) { $filter .= $wpdb->prepare(" AND afs_form_id = %s ", $get['fid']); } if(isset($get['pid']) && ($get['pid']!=-1)) { $filter .= $wpdb->prepare(" AND afs_post_id = %d ", $get['pid']); } if(isset($get['year']) && ($get['year']>0)) { $filter .= $wpdb->prepare(" AND year(afs_submitted) = %d ", $get['year']); } if(isset($get['month']) && ($get['month']>0)) { $filter .= $wpdb->prepare(" AND month(afs_submitted) = %d ", $get['month']); } if(isset($get['s'])) { $search = trim($get['s']); } if (isset($get['date_from']) && $get['date_from'] !== '') { $filter .= $wpdb->prepare(" AND afs_submitted >= %s ", $get['date_from']); } if (isset($get['date_to']) && $get['date_to'] !== '') { $filter .= $wpdb->prepare(" AND afs_submitted < %s ", $get['date_to']); } $columns = $this->get_columns(); $hidden = get_hidden_columns($hook_suffix); // Build sortable columns: all main DB columns + all field columns $sortable = array( 'ID' => array('ID', true), 'form_title' => array('form_title', false), 'form_id' => array('form_id', false), 'pid' => array('pid', false), 'ip' => array('ip', false), 'uri' => array('uri', false), 'referrer' => array('referrer', false), 'lang' => array('lang', false), 'created' => array('created', true), 'submitted' => array('submitted', true), 'lead_status' => array('lead_status', false), ); foreach ($columns as $key => $label) { if (strpos($key, '_field_') === 0) { $sortable[$key] = array($key, false); } } $this->_column_headers = array($columns, $hidden, $sortable); $current_page = $this->get_pagenum(); if ($all) { $limit = ''; } else { $limit = ($current_page - 1) * $per_page; $limit = $wpdb->prepare("LIMIT %d, %d", $limit, $per_page); } $forms_data = get_option('accua_forms_saved_forms', array()); if ($del) { $afs_status_cond = 'afs_status < 0'; } else { $afs_status_cond = 'afs_status >= 0'; } if (isset($get['lead_status'])) { $afs_lead_status_cond = $wpdb->prepare(" AND afs_lead_status = %d ", $get['lead_status']); } else { $afs_lead_status_cond = ''; } if($search !== '') { // Escape special LIKE characters (% and _) in user input, then wrap with wildcards $like_search = '%' . $wpdb->esc_like($search) . '%'; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table prefix is safe $sql_search_where = $wpdb->prepare( " AND ( afs_ip LIKE %s OR afs_uri LIKE %s OR afs_referrer LIKE %s OR afs_lang LIKE %s OR afs_created LIKE %s OR afs_submitted LIKE %s OR afs_id LIKE %s OR afs_id IN ( SELECT DISTINCT (afsv_sub_id) FROM `{$wpdb->prefix}accua_forms_submissions_values` WHERE afsv_value LIKE %s) ) ", $like_search, $like_search, $like_search, $like_search, $like_search, $like_search, $like_search, $like_search ); } else { $sql_search_where = ''; } // Sorting $orderby_sql = 'afs_id'; $order_sql = 'DESC'; $sort_join = ''; $null_sort = ''; if (isset($get['order']) && in_array(strtoupper($get['order']), array('ASC', 'DESC'), true)) { $order_sql = strtoupper($get['order']); } if (isset($get['orderby']) && $get['orderby'] !== '') { $main_col_map = array( 'ID' => 'afs_id', 'form_title' => 'afs_form_id', 'form_id' => 'afs_form_id', 'pid' => 'afs_post_id', 'ip' => 'afs_ip', 'uri' => 'afs_uri', 'referrer' => 'afs_referrer', 'lang' => 'afs_lang', 'created' => 'afs_created', 'submitted' => 'afs_submitted', 'lead_status' => 'afs_lead_status', ); $orderby_param = $get['orderby']; if (isset($main_col_map[$orderby_param])) { $orderby_sql = $main_col_map[$orderby_param]; } elseif (strpos($orderby_param, '_field_') === 0 && isset($columns[$orderby_param])) { $field_id = substr($orderby_param, 7); // strip '_field_' prefix // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table prefix is safe $sort_join = $wpdb->prepare( " LEFT JOIN `{$wpdb->prefix}accua_forms_submissions_values` AS sort_field ON sort_field.afsv_sub_id = afs_id AND sort_field.afsv_field_id = %s ", $field_id ); $orderby_sql = 'sort_field.afsv_value'; // Push NULLs (submissions without this field) to the end regardless of sort direction $null_sort = 'sort_field.afsv_value IS NULL,'; } } // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- All sort variables are validated against allowlists or prepared $query1 = "SELECT SQL_CALC_FOUND_ROWS afs_id AS ID, afs_form_id AS form_id, afs_post_id AS pid, afs_ip AS ip, afs_uri AS uri, afs_referrer AS referrer, afs_lang AS lang, afs_created AS created, afs_submitted AS submitted, afs_lead_status AS lead_status FROM `{$wpdb->prefix}accua_forms_submissions` {$sort_join} WHERE {$afs_status_cond} {$afs_lead_status_cond} {$filter} {$sql_search_where} ORDER BY {$null_sort} {$orderby_sql} {$order_sql}, afs_id DESC {$limit}"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- All components pre-prepared using $wpdb->prepare and esc_like $data1 = $wpdb->get_results($query1, ARRAY_A); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Built-in MySQL function $total_items = $wpdb->get_var('SELECT FOUND_ROWS()'); $data = array(); $submissions = array(); foreach ($data1 as $row) { $fid = $row['form_id']; if (isset($forms_data[$fid]['title']) && (trim($forms_data[$fid]['title']) !== '')) { $row['form_title'] = $forms_data[$fid]['title']; } else { $row['form_title'] = $fid; } $sid = (int) $row['ID']; foreach($row as $k => $v) { $row[$k] = esc_html($v); } $data[$sid] = $row; $submissions[] = $sid; } if ($submissions) { // Create dynamic placeholders for each ID $placeholders = implode(',', array_fill(0, count($submissions), '%d')); // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Dynamic placeholders, values are integers $query2 = $wpdb->prepare( "SELECT * FROM `{$wpdb->prefix}accua_forms_submissions_values` WHERE afsv_sub_id IN ($placeholders)", ...$submissions ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared above $data2 = $wpdb->get_results($query2, OBJECT); if ($data2) { foreach ($data2 as $row) { switch ($row->afsv_type) { case 'file' : $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 ($this->export_xls) { /* imposto token segreto 32 caratteri */ $token_xls = accua_forms_generate_download_token($row->afsv_sub_id); $url .= '&html=1&token='.$token_xls; } $url = esc_url($url); $filename = esc_attr($row->afsv_value); $fielddata = "{$filename}"; break; case 'colorpicker': if ($row->afsv_value === '') { $fielddata = ''; } else { $value_esc = esc_attr($row->afsv_value); $fielddata = " $value_esc"; } break; case 'password': case 'password-and-confirm': case 'hashed-password': $fielddata = ($row->afsv_value !== '') ? '••••' : ''; break; case 'token': $fielddata = ''; break; default: $fielddata = esc_attr($row->afsv_value); } $data[$row->afsv_sub_id]['_field_'.$row->afsv_field_id] = $fielddata; } } } $this->items = $data; $this->set_pagination_args( array( 'total_items' => $total_items, 'per_page' => $per_page, 'total_pages' => ceil($total_items/$per_page) ) ); } function process_bulk_action(){ $current_action = $this->current_action(); if ($current_action) { check_admin_referer('bulk-submissions'); //check nonce generated for 'bulk-'.$this->_args['plural'] if ('delete' === $current_action) { $trashed = array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Values are cast to int if ((!empty($_GET['submission'])) && is_array($_GET['submission'])) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Value cast to int foreach ($_GET['submission'] as $i) { $i = (int)$i; $trashed[$i] = $i; } } if ($trashed) { global $wpdb; // Create placeholders for each ID and prepare the query safely $placeholders = implode(',', array_fill(0, count($trashed), '%d')); // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Dynamic placeholders, values cast to int $query = $wpdb->prepare( "UPDATE `{$wpdb->prefix}accua_forms_submissions` SET afs_status = -1 WHERE afs_id IN ($placeholders)", array_values($trashed) ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared above $res = $wpdb->query($query); if ($res === false) { $this->set_message(__("Error moving submissions to trash.", 'contact-forms') ); } else if ($res == 1) { $this->set_message(__("Moved 1 submission to trash.", 'contact-forms') ); } else { $this->set_message(strtr(__("Moved %res submissions to trash.", 'contact-forms'), array('%res' => $res)) ); } } else { $this->set_message(__("No submission selected.", 'contact-forms') ); } } else if ('shred' === $current_action) { $shredded = array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Values are cast to int if ((!empty($_GET['submission'])) && is_array($_GET['submission'])) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Value cast to int foreach ($_GET['submission'] as $i) { $i = (int)$i; $shredded[$i] = $i; } } if ($shredded) { $shredded = array_values($shredded); $placeholders = implode(',', array_fill(0, count($shredded), '%d')); global $wpdb; // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Dynamic placeholders, values cast to int $query = $wpdb->prepare( "DELETE FROM `{$wpdb->prefix}accua_forms_submissions` WHERE afs_id IN ($placeholders)", ...$shredded ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared above $res = $wpdb->query($query); if ($res === false) { $this->set_message(__('Error deleting submissions.', 'contact-forms') ); } else { // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Dynamic placeholders, values cast to int $query2 = $wpdb->prepare( "DELETE FROM `{$wpdb->prefix}accua_forms_submissions_values` WHERE afsv_sub_id IN ($placeholders)", ...$shredded ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared above $res2 = $wpdb->query($query2); if ($res == 1) { $this->set_message(__("Deleted 1 submission.", 'contact-forms') ); } else { $this->set_message(strtr(__("Deleted %res submissions.", 'contact-forms'), array('%res' => $res)) ); } } } else { $this->set_message(__("No submission selected.", 'contact-forms') ); } } else if ('anonymize' === $current_action) { $anonymized = array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Values are cast to int if ((!empty($_GET['submission'])) && is_array($_GET['submission'])) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Value cast to int foreach ($_GET['submission'] as $i) { $i = (int)$i; $anonymized[$i] = $i; } } if ($anonymized) { $count = 0; foreach ($anonymized as $sub_id) { if (accua_forms_erase_submission($sub_id, 'anonymize')) { $count++; } } if ($count == 1) { $this->set_message(__('Anonymized 1 submission.', 'contact-forms') ); } else { $this->set_message(strtr(__('Anonymized %res submissions.', 'contact-forms'), array('%res' => $count)) ); } } else { $this->set_message(__('No submission selected.', 'contact-forms') ); } } else if ('restore' === $current_action) { $restored = array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Values are cast to int if ((!empty($_GET['submission'])) && is_array($_GET['submission'])) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Value cast to int foreach ($_GET['submission'] as $i) { $i = (int)$i; $restored[$i] = $i; } } if ($restored) { $restored = array_values($restored); $placeholders = implode(',', array_fill(0, count($restored), '%d')); global $wpdb; // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Dynamic placeholders, values cast to int $query = $wpdb->prepare( "UPDATE `{$wpdb->prefix}accua_forms_submissions` SET afs_status = 0 WHERE afs_id IN ($placeholders)", ...$restored ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared above $res = $wpdb->query($query); if ($res === false) { $this->set_message(__("Error restoring submissions.", 'contact-forms') ); } else if ($res == 1) { $this->set_message(__("Restored 1 submission.", 'contact-forms') ); } else { $this->set_message(strtr(__("Restored %res submissions.", 'contact-forms'), array('%res' => $res)) ); } } else { $this->set_message(__('No submission selected.', 'contact-forms') ); } } } } /** * Render filter dropdowns and export buttons above the table. * @param string $which 'top' or 'bottom' */ protected function extra_tablenav($which) { if ($which !== 'top') { return; } global $wpdb; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter state $get = stripslashes_deep($_GET); $filter_form = isset($get['fid']) && $get['fid'] != '-1' ? sanitize_text_field($get['fid']) : null; $filter_post = isset($get['pid']) && $get['pid'] != '-1' ? (int) $get['pid'] : null; $filter_year = isset($get['year']) && $get['year'] > 0 ? (int) $get['year'] : null; $filter_month = isset($get['month']) && $get['month'] > 0 ? (int) $get['month'] : null; // Forms data $forms_data = get_option('accua_forms_saved_forms', array()); $saved_forms_id = array_keys($forms_data); if (!empty($saved_forms_id)) { $placeholders = implode(',', array_fill(0, count($saved_forms_id), '%s')); // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Dynamic placeholders $query = $wpdb->prepare( "SELECT DISTINCT afs_form_id FROM {$wpdb->prefix}accua_forms_submissions WHERE afs_form_id NOT IN ($placeholders) AND afs_status >= 0", ...$saved_forms_id ); // phpcs:enable // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $deleted_form_ids = $wpdb->get_col($query); } else { $deleted_form_ids = array(); } // Pages // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Static query $page_ids = $wpdb->get_col("SELECT DISTINCT afs_post_id FROM {$wpdb->prefix}accua_forms_submissions WHERE afs_post_id <> 0 AND afs_status >= 0"); // Year range $cur_year = (int) wp_date('Y'); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Static query $min_year = (int) $wpdb->get_var("SELECT YEAR(MIN(afs_submitted)) FROM {$wpdb->prefix}accua_forms_submissions"); if ($min_year <= 0) { $min_year = $cur_year; } ?>
prepare( "UPDATE `{$wpdb->prefix}accua_forms_submissions` SET afs_status = -1 WHERE afs_id IN ($placeholders)", ...$trashed_values ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared above $res = $wpdb->query($query); if ($res === false) { $listTable->set_message(__("Error moving submissions to trash.", 'contact-forms')); } else { $listTable->set_message(strtr(__("Moved %res submissions to trash.", 'contact-forms'), array('%res' => $res))); } } } wp_enqueue_script('jquery'); wp_enqueue_script('jquery-ui-core'); wp_enqueue_script('jquery-ui-sortable'); wp_enqueue_script('accua-forms-set-lead-status', plugins_url('assets/js/admin/set-lead-status.js', ACCUA_FORMS_FILE ), array( 'jquery' ), ACCUA_FORMS_JS_VERSION, true); $listTable->process_bulk_action(); $listTable->prepare_items(); // Separate removed fields into their own Screen Options section if (!empty($listTable->removed_columns)) { $screen_id = $listTable->screen->id; // Strip removed columns from the main "Columns" fieldset (priority 10, after WP_List_Table's priority 0) add_filter("manage_{$screen_id}_columns", function($columns) use ($listTable) { foreach ($listTable->removed_columns as $key => $clean_name) { unset($columns[$key]); } return $columns; }, 10); // Render removed columns in a separate collapsible fieldset add_filter('screen_settings', function($settings) use ($listTable) { $hidden = get_hidden_columns(get_current_screen()); $count = count($listTable->removed_columns); ob_start(); ?>
removed_columns as $column => $clean_name) { $id = "$column-hide"; ?>
$export_params, 'exportNonce' => wp_create_nonce( 'accua_forms_export_excel' ), ) ); ?>

get_message() !== null) : ?>

get_message()); ?>

views(); ?>
search_box(__('Search', 'contact-forms'), 'search_id'); ?> display(); ?>