PluginProbe
Contact Forms by Cimatti / 2.3.5
Contact Forms by Cimatti v2.3.5
2.3.6 2.3.5 2.3.0 2.2.32 2.2.4 2.2.0 2.1.2 2.1.1 trunk 1.0 1.1 1.2 1.2.1 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 All 62 releases
contact-forms / admin / single-submission.php

single-submission.php in Contact Forms by Cimatti 2.3.5, at admin/single-submission.php

355 lines 19.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit;
3
4 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Submission detail page with prepared queries
5
6 function accua_forms_add_submission_note($id_sub, $note_text)
7 {
8 global $wpdb;
9 $time = time();
10 $current_date = gmdate('Y-m-d H:i:s', $time);
11
12 $current_user = wp_get_current_user();
13 $current_user_email = $current_user->user_email;
14
15 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is a template string with placeholders, prepared inline below
16 $query_add_note = "INSERT INTO {$wpdb->prefix}accua_forms_submissions_notes (afsn_sub_id, afsn_date, afsn_text, afsn_user) VALUES (%d, %s, %s, %s)";
17
18 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom table requires direct query, query is prepared inline
19 if ($wpdb->query($wpdb->prepare($query_add_note, $id_sub, $current_date, sanitize_textarea_field($note_text), $current_user_email)) === false) {
20 return false;
21 }
22 return array(
23 'date' => $current_date,
24 'user' => $current_user->display_name,
25 );
26 }
27 function accua_forms_count_submission_note($id_sub)
28 {
29 global $wpdb;
30 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is a template string with placeholders, prepared inline below
31 $query_data_submission_notes = "SELECT DISTINCT COUNT(afsn_sub_id)
32 FROM `{$wpdb->prefix}accua_forms_submissions_notes`
33 WHERE `afsn_sub_id` = %d";
34 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom table requires direct query, query is prepared inline
35 $count_submissions_form = $wpdb->get_var($wpdb->prepare($query_data_submission_notes, $id_sub));
36 return $count_submissions_form;
37 }
38 function accua_forms_return_submission_note($id_sub)
39 {
40 global $wpdb;
41 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is a template string with placeholders, prepared inline below
42 $query_data_submission_notes = "SELECT *
43 FROM `{$wpdb->prefix}accua_forms_submissions_notes`
44 WHERE `afsn_sub_id` = %d
45 ORDER BY `afsn_date` DESC";
46
47 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom table requires direct query, query is prepared inline
48 $query_submission_notes = $wpdb->get_results($wpdb->prepare($query_data_submission_notes, $id_sub));
49
50 $del_nonce = wp_create_nonce( "submission_{$id_sub}_note_del" );
51 foreach ($query_submission_notes as $value) {
52 $note_user = get_user_by( 'email', $value->afsn_user );
53 $note_user_name = $note_user ? $note_user->display_name : $value->afsn_user;
54 echo '<div class="accua-note">
55 <div class="accua-note-content">' . esc_html($value->afsn_text) . '</div>
56 <p class="accua-note-meta">' . esc_html($value->afsn_date) . '<br>' . esc_html($note_user_name) . '
57 <span class="row-actions">
58 <button type="button" class="button-link accua-delete-note" data-subid="' . (int) $value->afsn_sub_id . '" data-date="' . esc_attr($value->afsn_date) . '" data-nonce="' . esc_attr($del_nonce) . '">' . esc_html__('Delete', 'contact-forms') . '</button>
59 </span>
60 </p>
61 </div>';
62 }
63 }
64
65 function accua_forms_delete_submission_note($sid, $date)
66 {
67 global $wpdb;
68 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is a template string with placeholders, prepared inline below
69 $query_delete_notes = "DELETE FROM `{$wpdb->prefix}accua_forms_submissions_notes` WHERE `afsn_sub_id` = %d AND `afsn_date` = %s";
70 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom table requires direct query, query is prepared inline
71 return $wpdb->query($wpdb->prepare($query_delete_notes, $sid, $date)) !== false;
72 }
73
74
75 function accua_forms_single_submission($head = false)
76 {
77 wp_enqueue_style('accua-forms-admin', plugins_url('assets/css/admin.css', ACCUA_FORMS_FILE), array(), ACCUA_FORMS_CSS_VERSION);
78 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);
79 wp_enqueue_script('accua-forms-single-submission', plugins_url('assets/js/admin/single-submission.js', ACCUA_FORMS_FILE), array('jquery'), ACCUA_FORMS_JS_VERSION, true);
80 wp_localize_script('accua-forms-single-submission', 'accuaSingleSubmission', array(
81 'l10n' => array(
82 'confirmAnonymize' => __('Are you sure you want to anonymize this submission? Personal data will be replaced with placeholders. This cannot be undone.', 'contact-forms'),
83 'anonymizing' => __('Anonymizing…', 'contact-forms'),
84 'anonymize' => __('Anonymize', 'contact-forms'),
85 'errorAnonymize' => __('Error anonymizing submission. Please try again.', 'contact-forms'),
86 'confirmDeleteNote' => __('Are you sure you want to delete this note?', 'contact-forms'),
87 'deleteNote' => __('Delete', 'contact-forms'),
88 'errorNote' => __('Error saving note. Please try again.', 'contact-forms'),
89 ),
90 ));
91 global $wpdb;
92 if (isset($_GET['sid'])) {
93 $sid = (int) $_GET['sid'];
94 }
95
96 if ($head === true) {
97 return;
98 }
99 if ($sid) {
100 // Handle GET-based trash action (WP standard link pattern)
101 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified below before processing
102 if ( isset( $_GET['action'] ) && $_GET['action'] === 'trash' ) {
103 check_admin_referer( 'del_sub_form_' . $sid );
104 accua_forms_trash_submission( (int) $sid ); // Use echo-free function so redirect header can be sent
105 wp_safe_redirect( admin_url( 'admin.php?page=accua_forms_submissions_list&trashed=1' ) );
106 exit;
107 }
108 // Handle GET-based restore action
109 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified below before processing
110 if ( isset( $_GET['action'] ) && $_GET['action'] === 'restore' ) {
111 check_admin_referer( 'restore_sub_form_' . $sid );
112 accua_forms_restore_submission( (int) $sid );
113 wp_safe_redirect( admin_url( 'admin.php?page=accua_forms_submissions_list&restored=1' ) );
114 exit;
115 }
116
117 $submission_row = $wpdb->get_row($wpdb->prepare("SELECT `afs_status`, `afs_anonymized` FROM `{$wpdb->prefix}accua_forms_submissions` WHERE `afs_id` = %d", $sid));
118 $form_status = $submission_row ? (int) $submission_row->afs_status : null;
119 $is_anonymized = $submission_row ? (int) $submission_row->afs_anonymized : 0;
120 $is_trashed = ( $form_status === -1 );
121 ?>
122 <div id="accua_forms_submissions_list_page" class="accua_forms_admin_page wrap">
123 <h1 class="wp-heading-inline"><?php
124 /* translators: %d: submission ID */
125 printf( esc_html__( 'Submission #%d', 'contact-forms' ), absint( $sid ) );
126 ?></h1>
127 <?php
128 $prev = $wpdb->get_var($wpdb->prepare("SELECT `afs_id` FROM `{$wpdb->prefix}accua_forms_submissions` WHERE `afs_status` >= 0 AND `afs_id` < %d ORDER BY `afs_id` DESC LIMIT 1", $sid));
129 $next = $wpdb->get_var($wpdb->prepare("SELECT `afs_id` FROM `{$wpdb->prefix}accua_forms_submissions` WHERE `afs_status` >= 0 AND `afs_id` > %d ORDER BY `afs_id` ASC LIMIT 1", $sid));
130 if ($prev !== null) {
131 echo '<a class="page-title-action" href="' . esc_url( admin_url( 'admin.php?page=accua_forms_submissions_list&sid=' . absint($prev) ) ) . '">&#8249; ' . esc_html__('Previous', 'contact-forms') . '</a>';
132 }
133 if ($next !== null) {
134 echo '<a class="page-title-action" href="' . esc_url( admin_url( 'admin.php?page=accua_forms_submissions_list&sid=' . absint($next) ) ) . '">' . esc_html__('Next', 'contact-forms') . ' &#8250;</a>';
135 }
136 ?>
137 <a class="page-title-action" href="<?php echo esc_url( admin_url( 'admin.php?page=accua_forms_submissions_list' ) ); ?>"><?php esc_html_e('All submissions', 'contact-forms'); ?></a>
138 <hr class="wp-header-end">
139
140 <?php if ( $is_anonymized ) { ?>
141 <div class="notice notice-warning inline">
142 <p><?php esc_html_e('This submission has been anonymized. Personal data has been replaced with placeholders.', 'contact-forms'); ?></p>
143 </div>
144 <?php } ?>
145
146 <?php
147 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is a template string with placeholders, prepared inline below
148 $query_data_submission = "SELECT *
149 FROM `{$wpdb->prefix}accua_forms_submissions`
150 WHERE `afs_id` = %d";
151
152 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is a template string with placeholders, prepared inline below
153 $query_data_submission_value = "SELECT *
154 FROM `{$wpdb->prefix}accua_forms_submissions_values`
155 WHERE `afsv_sub_id` = %d";
156
157 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom table requires direct query, query is prepared inline
158 $data_submission = $wpdb->get_row($wpdb->prepare($query_data_submission, $sid));
159 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom table requires direct query, query is prepared inline
160 $data_submission_values = $wpdb->get_results($wpdb->prepare($query_data_submission_value, $sid));
161 ?>
162
163 <?php if ( $data_submission ) { ?>
164 <?php
165 // Resolve form name
166 $forms_data = get_option( 'accua_forms_saved_forms', array() );
167 $form_name = isset( $forms_data[ $data_submission->afs_form_id ]['title'] )
168 ? $forms_data[ $data_submission->afs_form_id ]['title']
169 : '#' . $data_submission->afs_form_id;
170
171 // Resolve page title
172 $page_title = '';
173 if ( (int) $data_submission->afs_post_id > 0 ) {
174 $page_title = get_the_title( (int) $data_submission->afs_post_id );
175 }
176 ?>
177 <div id="poststuff">
178 <div id="post-body" class="metabox-holder columns-2">
179 <div id="post-body-content">
180 <div class="postbox">
181 <div class="postbox-header"><h2><?php esc_html_e('Submitted fields', 'contact-forms'); ?></h2></div>
182 <div class="inside">
183 <?php
184 // Build a field_id => label map from global fields
185 $avail_fields = get_option( 'accua_forms_avail_fields', array() );
186 $field_labels = array();
187 foreach ( $avail_fields as $field ) {
188 if ( ! empty( $field['id'] ) && ! empty( $field['name'] ) ) {
189 $field_labels[ $field['id'] ] = $field['name'];
190 }
191 }
192 ?>
193 <table class="widefat fixed striped">
194 <?php foreach ($data_submission_values as $value) {
195 // Skip internal/structural fields
196 if ( str_starts_with( $value->afsv_field_id, '__' ) || str_starts_with( $value->afsv_field_id, '_accua_' ) ) {
197 continue;
198 }
199
200 $label = $field_labels[ $value->afsv_field_id ] ?? ucwords( str_replace( array( '-', '_' ), ' ', $value->afsv_field_id ) );
201
202 if ($value->afsv_type === 'file') {
203 $fieldid = rawurlencode($value->afsv_field_id);
204 $filename = rawurlencode($value->afsv_value);
205 $url = admin_url('admin-ajax.php') . "?action=accua_forms_download_submitted_file&subid={$value->afsv_sub_id}&field={$fieldid}&file={$filename}&nonce=" . wp_create_nonce('accua_forms_download_nonce') . "&_wpnonce=" . wp_create_nonce('download_file_' . $value->afsv_sub_id . '_' . $fieldid);
206 echo '<tr>';
207 echo '<td>' . esc_html( $label ) . '</td>';
208 echo '<td><a href="' . esc_url( $url ) . '" target="_blank">' . esc_html( $value->afsv_value ) . '</a></td>';
209 echo '</tr>';
210 } else {
211 echo '<tr>';
212 echo '<td>' . esc_html( $label ) . '</td>';
213 echo '<td class="accua-field-value">' . esc_html( $value->afsv_value ) . '</td>';
214 echo '</tr>';
215 }
216 } ?>
217 </table>
218 </div>
219 </div>
220
221 <div class="postbox">
222 <div class="postbox-header"><h2><?php esc_html_e('Details', 'contact-forms'); ?></h2></div>
223 <div class="inside">
224 <table class="widefat fixed striped">
225 <tr>
226 <td><?php esc_html_e('Form', 'contact-forms'); ?></td>
227 <td><a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=accua_forms_list&fid=' . absint( $data_submission->afs_form_id ) ), 'edit_posts' ) ); ?>"><?php echo esc_html( $form_name ); ?></a></td>
228 </tr>
229 <?php if ( $data_submission->afs_ip ) { ?>
230 <tr>
231 <td><?php esc_html_e('IP', 'contact-forms'); ?></td>
232 <td><?php echo esc_html( $data_submission->afs_ip ); ?></td>
233 </tr>
234 <?php } ?>
235 <?php if ( $data_submission->afs_lang ) { ?>
236 <tr>
237 <td><?php esc_html_e('Language', 'contact-forms'); ?></td>
238 <td><?php echo esc_html( $data_submission->afs_lang ); ?></td>
239 </tr>
240 <?php } ?>
241 <?php if ( $data_submission->afs_uri ) { ?>
242 <tr>
243 <td><?php esc_html_e('URI', 'contact-forms'); ?></td>
244 <td class="accua-field-value"><a href="<?php echo esc_url( home_url( $data_submission->afs_uri ) ); ?>" target="_blank"><?php echo esc_html( $data_submission->afs_uri ); ?></a></td>
245 </tr>
246 <?php } ?>
247 <?php if ( $data_submission->afs_referrer ) { ?>
248 <tr>
249 <td><?php esc_html_e('Referrer', 'contact-forms'); ?></td>
250 <td class="accua-field-value"><a href="<?php echo esc_url( $data_submission->afs_referrer ); ?>" target="_blank"><?php echo esc_html( $data_submission->afs_referrer ); ?></a></td>
251 </tr>
252 <?php }
253 // Driven by what this submission recorded, never by the fields the
254 // form currently has: a submission keeps showing its score after the
255 // reCAPTCHA v3 field is removed from the form, and one stored before
256 // the field existed shows no row at all rather than an empty value.
257 // A score of exactly 0 is falsy but real, hence isset() + is_numeric().
258 $submission_stats = $data_submission->afs_stats ? json_decode( $data_submission->afs_stats, true ) : array();
259 if ( isset( $submission_stats['recaptcha3_score'] ) && is_numeric( $submission_stats['recaptcha3_score'] ) ) { ?>
260 <tr class="accua-recaptcha3-score">
261 <td><?php esc_html_e('reCAPTCHA v3 score', 'contact-forms'); ?></td>
262 <td><?php echo esc_html( number_format_i18n( (float) $submission_stats['recaptcha3_score'], 2 ) ); ?>
263 <small><?php esc_html_e('(0.0 = almost certainly a bot, 1.0 = almost certainly a person)', 'contact-forms'); ?></small></td>
264 </tr>
265 <?php }
266 // Why the submission was classified as spam, when something other
267 // than the captcha did it. Read from what this submission recorded,
268 // so it keeps saying so after the blocklist entry that caught it is
269 // removed from the settings.
270 if ( isset( $submission_stats['spam_reason'] ) && 'email_blocklist' === $submission_stats['spam_reason'] ) { ?>
271 <tr class="accua-spam-reason">
272 <td><?php esc_html_e('Spam check', 'contact-forms'); ?></td>
273 <td><?php esc_html_e('Marked as spam automatically: the email address is on the blocklist.', 'contact-forms'); ?>
274 <small><?php
275 /* translators: %s: name of the plugin settings tab that holds the blocklist */
276 printf( esc_html__( 'Contact Forms settings, %s tab.', 'contact-forms' ), esc_html__( 'Other settings', 'contact-forms' ) );
277 ?></small></td>
278 </tr>
279 <?php } ?>
280 </table>
281 </div>
282 </div>
283
284 </div><!-- #post-body-content -->
285
286 <div id="postbox-container-1" class="postbox-container">
287 <div class="postbox">
288 <div class="postbox-header"><h2><?php esc_html_e('Submission', 'contact-forms'); ?></h2></div>
289 <div class="inside">
290 <?php if ( $data_submission->afs_created && $data_submission->afs_created !== $data_submission->afs_submitted ) { ?>
291 <p>
292 <strong><?php esc_html_e('Opened', 'contact-forms'); ?></strong><br>
293 <?php echo esc_html( wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $data_submission->afs_created ) ) ); ?>
294 </p>
295 <?php } ?>
296 <p>
297 <strong><?php esc_html_e('Submitted', 'contact-forms'); ?></strong><br>
298 <?php echo esc_html( wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $data_submission->afs_submitted ) ) ); ?>
299 </p>
300 <?php if ( $page_title ) { ?>
301 <p>
302 <strong><?php esc_html_e('Page', 'contact-forms'); ?></strong><br>
303 <a href="<?php echo esc_url( get_permalink( (int) $data_submission->afs_post_id ) ); ?>" target="_blank"><?php echo esc_html( $page_title ); ?></a>
304 </p>
305 <?php } ?>
306 <?php if ( ! $is_anonymized ) { ?>
307 <p><a href="#" id="accua-forms-anonymize-btn" data-subid="<?php echo absint($sid); ?>" data-nonce="<?php echo esc_attr( wp_create_nonce( "anonymize_sub_{$sid}" ) ); ?>"><?php esc_html_e('Anonymize personal data', 'contact-forms'); ?></a></p>
308 <?php } ?>
309 <p>
310 <?php if ( $is_trashed ) { ?>
311 <a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=accua_forms_submissions_list&sid=' . absint($sid) . '&action=restore' ), 'restore_sub_form_' . $sid ) ); ?>"><?php esc_html_e('Restore from Trash', 'contact-forms'); ?></a>
312 <?php } else { ?>
313 <a class="submitdelete deletion" href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=accua_forms_submissions_list&sid=' . absint($sid) . '&action=trash' ), 'del_sub_form_' . $sid ) ); ?>" onclick='return window.confirm("<?php echo esc_js(__('Are you sure you want to trash this submission?', 'contact-forms')); ?>");'><?php esc_html_e('Move to Trash', 'contact-forms'); ?></a>
314 <?php } ?>
315 </p>
316 </div>
317 </div>
318 <div class="postbox">
319 <div class="postbox-header"><h2><?php esc_html_e('Lead status', 'contact-forms'); ?></h2></div>
320 <div class="inside">
321 <?php
322 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Function returns escaped HTML select element
323 echo accua_forms_select_lead_status( $data_submission->afs_id, $data_submission->afs_lead_status );
324 ?>
325 </div>
326 </div>
327 <div class="postbox">
328 <div class="postbox-header"><h2><?php esc_html_e('Notes', 'contact-forms'); ?></h2></div>
329 <div class="inside">
330 <div id="accua-notes-form">
331 <textarea id="note_site_user" rows="2" placeholder="<?php esc_attr_e('Add a note…', 'contact-forms'); ?>"></textarea>
332 <button type="button" id="accua-add-note-btn" class="button" data-subid="<?php echo absint($sid); ?>" data-nonce="<?php echo esc_attr( wp_create_nonce( "submission_{$sid}_note_add" ) ); ?>"><?php esc_html_e('Add note', 'contact-forms'); ?></button>
333 </div>
334 <div id="accua-notes-list">
335 <?php
336 $conta_note = accua_forms_count_submission_note($sid);
337 if ($conta_note > 0) {
338 accua_forms_return_submission_note($sid);
339 }
340 ?>
341 </div>
342 </div>
343 </div>
344 </div><!-- #postbox-container-1 -->
345 </div><!-- #post-body -->
346 </div><!-- #poststuff -->
347
348 <?php } ?>
349 </div>
350 <?php
351 }
352 }
353
354 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
355