PluginProbe
Contact Forms by Cimatti / 2.2.32
Contact Forms by Cimatti v2.2.32
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 / fields-page.php

fields-page.php in Contact Forms by Cimatti 2.2.32, at admin/fields-page.php

461 lines 20.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Fields admin page — rendering, request handlers, and helpers.
4 *
5 * @package ContactForms
6 * @since 2.2.5
7 */
8
9 defined( 'ABSPATH' ) || exit;
10
11 /**
12 * Enqueue scripts for the Fields admin page.
13 */
14 function accua_forms_fields_page_enqueue_scripts() {
15 wp_enqueue_script(
16 'accua-forms-fields-page',
17 plugins_url( 'assets/js/admin/fields-page.js', ACCUA_FORMS_FILE ),
18 array( 'jquery', 'wp-a11y' ),
19 ACCUA_FORMS_JS_VERSION,
20 true
21 );
22
23 wp_localize_script( 'accua-forms-fields-page', 'accuaFieldsPage', array(
24 'l10n' => array(
25 'allowedExtensionsLabel' => __( 'Allowed extensions', 'contact-forms' ) . ':',
26 'allowedExtensionsHelp' => __( 'Enter one extension per line without dot (e.g. pdf, jpg, docx). Leave blank to use defaults.', 'contact-forms' ),
27 'allowedValuesLabel' => __( 'Allowed values', 'contact-forms' ) . ':',
28 'allowedValuesHelp' => __( 'Enter one value per line, in the format key|label. The key is the value stored in the database. The label is optional.', 'contact-forms' ),
29 'errorLabelRequired' => __( 'Field label is required.', 'contact-forms' ),
30 'errorSlugRequired' => __( 'Field slug is required.', 'contact-forms' ),
31 'errorAllowedValuesRequired' => __( 'Allowed values are required for this field type.', 'contact-forms' ),
32 ),
33 ) );
34 }
35
36 /**
37 * Get the available field types.
38 *
39 * @return array Associative array of type_id => translated label.
40 */
41 function accua_forms_fields_get_types() {
42 $types = array(
43 'textfield' => __( 'Text Field', 'contact-forms' ),
44 'textarea' => __( 'Text Area', 'contact-forms' ),
45 'email' => __( 'Email', 'contact-forms' ),
46 'autoreply_email' => __( 'Autoreply Email', 'contact-forms' ),
47 'telephone' => __( 'Telephone', 'contact-forms' ),
48 'checkbox' => __( 'Checkbox', 'contact-forms' ),
49 'select' => __( 'Select', 'contact-forms' ),
50 'radio' => __( 'Radio buttons', 'contact-forms' ),
51 'multiselect' => __( 'Multiple selections area', 'contact-forms' ),
52 'multicheckbox' => __( 'Multiple checkboxes', 'contact-forms' ),
53 'post-select' => __( 'Post select', 'contact-forms' ),
54 'post-multicheckbox' => __( 'Multiple post checkboxes', 'contact-forms' ),
55 'colorpicker' => __( 'Color picker', 'contact-forms' ),
56 'hidden' => __( 'Hidden value', 'contact-forms' ),
57 'file' => __( 'File upload', 'contact-forms' ),
58 'submit' => __( 'Submit button', 'contact-forms' ),
59 'html' => __( 'Custom HTML', 'contact-forms' ),
60 'captcha' => __( 'Captcha', 'contact-forms' ),
61 'password' => 'Password',
62 'password-and-confirm' => __( 'Password and password confirmation', 'contact-forms' ),
63 'date' => __( 'Date', 'contact-forms' ),
64 );
65
66 /**
67 * Filter the available field types.
68 *
69 * @param array $types Associative array of type_id => label.
70 */
71 return apply_filters( 'accua_forms_field_types', $types );
72 }
73
74 /**
75 * Validate and filter a date string (YYYY-MM-DD).
76 *
77 * @param string $value Date string.
78 * @return string Valid date or empty string.
79 */
80 function accua_forms_filter_date( $value ) {
81 if ( ( $value !== '' ) && preg_match( '/^\d{4}-\d{2}-\d{2}$/', $value ) ) {
82 try {
83 $date = new DateTime( $value );
84 if ( $date ) {
85 return $value;
86 }
87 } catch ( Exception $e ) {
88 }
89 }
90 return '';
91 }
92
93 /**
94 * Validate and filter field data from form submission.
95 *
96 * @param array $post Form POST data.
97 * @param array $old_data Previous field data (for edits).
98 * @return array { 'data' => array, 'valid' => bool, 'message' => string }
99 */
100 function accua_forms_fields_filter_values( $post, $old_data = array() ) {
101 $data = array(
102 'version' => 2,
103 'id' => $post['form-field-id'],
104 'name' => $post['form-field-name'],
105 'type' => $post['form-field-type'],
106 'description' => $post['form-field-description'],
107 'default_value' => $post['form-field-default-value'],
108 'default_date_value' => $post['form-field-default-date-value'],
109 'allowed_values' => $post['form-field-allowed-values'],
110 'allowed_extensions' => '',
111 'min_date' => $post['form-field-min-of-date'],
112 'max_date' => $post['form-field-max-of-date'],
113 'custom_required_message' => sanitize_text_field( $post['form-field-custom-required-message'] ),
114 'custom_format_message' => sanitize_text_field( $post['form-field-custom-format-message'] ),
115 );
116 $valid = true;
117 $message = '';
118
119 if ( trim( $data['name'] ) === '' ) {
120 $message .= '<p>' . __( 'Field label is required', 'contact-forms' ) . '</p>';
121 $valid = false;
122 }
123
124 $types = accua_forms_fields_get_types();
125 if ( ! isset( $types[ $data['type'] ] ) ) {
126 $message .= '<p>' . __( 'Invalid type', 'contact-forms' ) . '</p>';
127 $valid = false;
128 $data['type'] = 'textfield';
129 }
130
131 if ( ! current_user_can( 'unfiltered_html' ) ) {
132 $filter_fields = array( 'name', 'description', 'default_value', 'allowed_values' );
133 foreach ( $filter_fields as $k ) {
134 $data[ $k ] = wp_kses( $data[ $k ], 'post' );
135 }
136 }
137
138 if ( $data['type'] == 'file' ) {
139 $data['allowed_extensions'] = accua_forms_filter_extensions( $data['allowed_values'] );
140 }
141
142 // Require allowed values for types that need options
143 $types_needing_options = array( 'select', 'radio', 'multiselect', 'multicheckbox' );
144 if ( in_array( $data['type'], $types_needing_options ) && trim( $data['allowed_values'] ) === '' ) {
145 $message .= '<p>' . __( 'Allowed values are required for this field type', 'contact-forms' ) . '</p>';
146 $valid = false;
147 }
148
149 $dates = array(
150 'default_date_value' => __( 'Invalid default date', 'contact-forms' ),
151 'min_date' => __( 'Invalid min date', 'contact-forms' ),
152 'max_date' => __( 'Invalid max date', 'contact-forms' ),
153 );
154 foreach ( $dates as $k => $errormsg ) {
155 if ( $data[ $k ] !== '' ) {
156 $data[ $k ] = accua_forms_filter_date( $data[ $k ] );
157 if ( $data[ $k ] === '' ) {
158 $message .= '<p>' . $errormsg . '</p>';
159 $valid = false;
160 }
161 }
162 }
163
164 return array(
165 'data' => $data,
166 'valid' => $valid,
167 'message' => $message,
168 );
169 }
170
171 /**
172 * Render the Fields admin page (request handling + output).
173 */
174 function accua_forms_fields_page() {
175 $message = '';
176
177 $avail_fields = get_option( 'accua_forms_avail_fields', array() );
178
179 $default_form_values = array(
180 'version' => 1,
181 'id' => '',
182 'name' => '',
183 'type' => 'textfield',
184 'description' => '',
185 'default_value' => '',
186 'default_date_value' => '',
187 'allowed_values' => '',
188 'allowed_extensions' => '',
189 'min_date' => '',
190 'max_date' => '',
191 'custom_required_message' => '',
192 'custom_format_message' => '',
193 );
194
195 $editing = false;
196 $adding = true;
197 $message_type = '';
198
199 if ( ! empty( $_POST['action'] ) ) {
200 check_admin_referer( 'edit_form_field', '_wpnonce_edit_form_field' );
201 $post = stripslashes_deep( $_POST ) + $default_form_values;
202 switch ( $post['action'] ) {
203 case 'edit-form-field':
204 if ( empty( $avail_fields[ $post['form-field-id'] ] ) ) {
205 $message .= '<p>Field "' . esc_html( sanitize_text_field( $post['form-field-id'] ) ) . '" doesn\'t exists</p>';
206 $message_type = 'error';
207 } else {
208 if ( empty( $post['delete-field'] ) ) {
209 $filtered_data = accua_forms_fields_filter_values( $post, $avail_fields[ $post['form-field-id'] ] );
210 $message .= $filtered_data['message'];
211 if ( $filtered_data['valid'] ) {
212 $avail_fields[ $post['form-field-id'] ] = $filtered_data['data'];
213 /* translators: %s is the field slug */
214 $message .= '<p>' . sprintf( __( 'Field "%s" updated', 'contact-forms' ), esc_html( $post['form-field-id'] ) ) . '</p>';
215 $message_type = 'success';
216 update_option( 'accua_forms_avail_fields', $avail_fields );
217 do_action( 'accua_forms_field_updated', $avail_fields[ $post['form-field-id'] ] );
218 } else {
219 $message_type = 'error';
220 $editing = true;
221 $adding = false;
222 $default_form_values = $filtered_data['data'];
223 }
224 } else {
225 $deleting_field = $avail_fields[ $post['form-field-id'] ];
226 unset( $avail_fields[ $post['form-field-id'] ] );
227 /* translators: %s is the field slug */
228 $message .= '<p>' . sprintf( __( 'Field "%s" deleted', 'contact-forms' ), esc_html( $post['form-field-id'] ) ) . '</p>';
229 $message_type = 'success';
230 do_action( 'accua_forms_field_deleted', $deleting_field );
231 update_option( 'accua_forms_avail_fields', $avail_fields );
232 }
233 }
234 break;
235 case 'add-form-field':
236 $fill_form_fields = true;
237 $valid = true;
238 if ( empty( $post['form-field-id'] ) || ! preg_match( '/^[a-z0-9_-]+$/i', $post['form-field-id'] ) ) {
239 $message .= '<p>' . __( 'Only letters, numbers, hyphens, and underscores allowed in field slug', 'contact-forms' ) . '</p>';
240 $valid = false;
241 }
242 if ( substr( $post['form-field-id'], 0, 2 ) == '__' ) {
243 $message .= '<p>' . __( 'The field slug cannot start with two underscores (__)', 'contact-forms' ) . '</p>';
244 $valid = false;
245 }
246 if ( ! empty( $avail_fields[ $post['form-field-id'] ] ) ) {
247 /* translators: %s is the field slug */
248 $message .= sprintf( __( '<p>A field with slug "%s" already exists.</p><p>Field was not added.</p>', 'contact-forms' ), esc_html( $post['form-field-id'] ) );
249 $valid = false;
250 }
251 if ( strlen( $post['form-field-id'] ) > 70 ) {
252 $message .= '<p>' . __( 'The field slug cannot be longer than 70 characters', 'contact-forms' ) . '</p>';
253 $valid = false;
254 }
255 $filtered_data = accua_forms_fields_filter_values( $post );
256 $message .= $filtered_data['message'];
257 $valid = $valid && $filtered_data['valid'];
258 if ( $valid ) {
259 $fill_form_fields = false;
260 $avail_fields[ $post['form-field-id'] ] = $filtered_data['data'];
261 update_option( 'accua_forms_avail_fields', $avail_fields );
262 /* translators: %s is the field slug */
263 $message .= '<p>' . sprintf( __( 'Field "%s" created', 'contact-forms' ), esc_html( $post['form-field-id'] ) ) . '</p>';
264 $message_type = 'success';
265 do_action( 'accua_forms_field_added', $avail_fields[ $post['form-field-id'] ] );
266 } else {
267 $message_type = 'error';
268 }
269 if ( $fill_form_fields ) {
270 $editing = true;
271 $default_form_values = $filtered_data['data'];
272 }
273 break;
274 }
275 } elseif ( ! empty( $_GET['edit-fid'] ) ) {
276 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only field lookup
277 $fid = sanitize_text_field( wp_unslash( $_GET['edit-fid'] ) );
278 if ( empty( $avail_fields[ $fid ] ) ) {
279 /* translators: %s is the field slug */
280 $message .= sprintf( __( 'Field "%s" doesn\'t exists', 'contact-forms' ), esc_html( $fid ) );
281 } else {
282 $adding = false;
283 $editing = true;
284 $default_form_values = $avail_fields[ $fid ] + $default_form_values;
285 }
286 } elseif ( ! empty( $_GET['delete-fid'] ) ) {
287 $fid = sanitize_text_field( wp_unslash( $_GET['delete-fid'] ) );
288 check_admin_referer( 'delete_form_field_' . $fid );
289 if ( empty( $avail_fields[ $fid ] ) ) {
290 /* translators: %s is the field slug */
291 $message .= '<p>' . sprintf( __( 'Field "%s" doesn\'t exists', 'contact-forms' ), esc_html( $fid ) ) . '</p>';
292 $message_type = 'error';
293 } else {
294 $deleting_field = $avail_fields[ $fid ];
295 unset( $avail_fields[ $fid ] );
296 update_option( 'accua_forms_avail_fields', $avail_fields );
297 /* translators: %s is the field slug */
298 $message .= '<p>' . sprintf( __( 'Field "%s" deleted', 'contact-forms' ), esc_html( $fid ) ) . '</p>';
299 $message_type = 'success';
300 do_action( 'accua_forms_field_deleted', $deleting_field );
301 }
302 } elseif ( ! empty( $_GET['fields'] ) && is_array( $_GET['fields'] ) ) {
303 $bulk_action = '';
304 if ( isset( $_GET['action'] ) && $_GET['action'] !== '-1' ) {
305 $bulk_action = sanitize_text_field( wp_unslash( $_GET['action'] ) );
306 } elseif ( isset( $_GET['action2'] ) && $_GET['action2'] !== '-1' ) {
307 $bulk_action = sanitize_text_field( wp_unslash( $_GET['action2'] ) );
308 }
309 if ( 'delete' === $bulk_action ) {
310 check_admin_referer( 'bulk-fields' );
311 $fields_to_delete = array_map( 'sanitize_text_field', wp_unslash( $_GET['fields'] ) );
312 $deleted_count = 0;
313 foreach ( $fields_to_delete as $fid ) {
314 if ( ! empty( $avail_fields[ $fid ] ) ) {
315 do_action( 'accua_forms_field_deleted', $avail_fields[ $fid ] );
316 unset( $avail_fields[ $fid ] );
317 $deleted_count++;
318 }
319 }
320 if ( $deleted_count > 0 ) {
321 update_option( 'accua_forms_avail_fields', $avail_fields );
322 /* translators: %d is the number of deleted fields */
323 $message .= '<p>' . sprintf( _n( '%d field deleted.', '%d fields deleted.', $deleted_count, 'contact-forms' ), $deleted_count ) . '</p>';
324 $message_type = 'success';
325 }
326 }
327 }
328
329 if ( $default_form_values['version'] >= 2 && $default_form_values['type'] == 'file' ) {
330 // Show allowed_extensions value in allowed_values field
331 $default_form_values['allowed_values'] = $default_form_values['allowed_extensions'];
332 }
333
334 ?>
335 <div id="accua_forms_fields_page" class="accua_forms_admin_page wrap nosubsub">
336 <h1><?php esc_html_e( 'Contact Forms - Fields', 'contact-forms' ); ?></h1>
337 <?php if ( $message !== '' ) : ?>
338 <div id="ajax-response" class="notice notice-<?php echo $message_type === 'success' ? 'success' : 'error'; ?> is-dismissible"><?php echo wp_kses_post( $message ); ?></div>
339 <?php endif; ?>
340
341 <div id="col-container">
342
343 <div id="col-right">
344 <div class="col-wrap">
345 <?php
346 if ( ! $editing ) {
347 require_once __DIR__ . '/class-fields-list-table.php';
348 $fields_table = new Accua_Forms_Fields_List_Table();
349 $fields_table->prepare_items();
350 ?>
351 <form method="get">
352 <input type="hidden" name="page" value="accua_forms_fields" />
353 <?php $fields_table->display(); ?>
354 </form>
355 <?php } ?>
356
357 </div>
358 </div><!-- /col-right -->
359
360 <div id="col-left">
361 <div class="col-wrap">
362
363 <?php $types = accua_forms_fields_get_types(); ?>
364 <div class="form-wrap">
365 <h3><?php echo $adding ? esc_html__( 'Add new field', 'contact-forms' ) : esc_html__( 'Edit field', 'contact-forms' ); ?></h3>
366 <form id="addtag" method="post" action="admin.php?page=accua_forms_fields" class="validate">
367 <input type="hidden" name="action" value="<?php echo $adding ? 'add' : 'edit'; ?>-form-field" />
368 <?php wp_nonce_field( 'edit_form_field', '_wpnonce_edit_form_field' ); ?>
369
370 <div class="form-field form-required">
371 <label for="tag-name"><?php esc_html_e( 'Field label', 'contact-forms' ); ?></label>
372 <input name="form-field-name" id="tag-name" type="text" value="<?php echo esc_attr( $default_form_values['name'] ); ?>" size="40" aria-required="true" />
373 <p><?php esc_html_e( 'The name is how it appears on your site.', 'contact-forms' ); ?></p>
374 </div>
375 <div class="form-field<?php echo $adding ? ' form-required' : ''; ?>">
376 <label for="tag-slug"><?php esc_html_e( 'Field slug (identifier)', 'contact-forms' ); ?></label>
377 <input name="form-field-id" id="tag-slug" type="text" value="<?php echo esc_attr( $default_form_values['id'] ); ?>" <?php if ( ! $adding ) { echo 'disabled="disabled"'; } ?> size="40" <?php if ( $adding ) { echo 'aria-required="true"'; } ?> />
378 <?php if ( ! $adding ) { echo '<input type="hidden" name="form-field-id" value="' . esc_attr( $default_form_values['id'] ) . '" />'; } ?>
379 <p><?php esc_html_e( 'The &#8220;slug&#8221; is the URL-friendly version of the name. It is used as a unique identifier and cannot be changed. It is usually all lowercase and must contain only letters, numbers, and underscores.', 'contact-forms' ); ?></p>
380 </div>
381 <div class="form-field">
382 <label for="parent"><?php esc_html_e( 'Field type', 'contact-forms' ); ?></label>
383 <select class="postform" id="parent" name="form-field-type">
384 <?php
385 foreach ( $types as $typeid => $typename ) {
386 $selected = ( $default_form_values['type'] == $typeid ) ? 'selected="selected"' : '';
387 // phpcs:disable PluginCheck.CodeAnalysis.Heredoc.NotAllowed, WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
388 echo <<<EOT
389 <option value="{$typeid}" class="level-0" {$selected} >{$typename}</option>
390 EOT;
391 // phpcs:enable PluginCheck.CodeAnalysis.Heredoc.NotAllowed, WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
392 }
393 ?>
394 </select>
395 </div>
396 <div class="form-field">
397 <label for="tag-description"><?php esc_html_e( 'Field description', 'contact-forms' ); ?></label>
398 <textarea name="form-field-description" id="tag-description" rows="5" cols="40"><?php echo esc_textarea( $default_form_values['description'] ); ?></textarea>
399 <p><?php esc_html_e( 'The description is not prominent by default; however, some themes may show it.', 'contact-forms' ); ?></p>
400 </div>
401
402 <div class="form-field" id="field-section-default-value">
403 <label for="form-field-default-value"><?php esc_html_e( 'Default value(s)', 'contact-forms' ); ?>:</label>
404 <textarea name="form-field-default-value" id="form-field-default-value" rows="5" cols="40"><?php echo esc_textarea( $default_form_values['default_value'] ); ?></textarea>
405 <p><?php esc_html_e( 'For multiple default values in multiple select and multiple checkboxes, use | as separator.', 'contact-forms' ); ?></p>
406 </div>
407
408 <div class="form-field" id="field-section-allowed-values">
409 <label for="form-field-allowed-values" id="allowed-values-label"><?php esc_html_e( 'Allowed values', 'contact-forms' ); ?>:</label>
410 <textarea rows="5" cols="40" name="form-field-allowed-values" id="form-field-allowed-values"><?php echo esc_textarea( $default_form_values['allowed_values'] ); ?></textarea>
411 <p id="allowed-values-help"><?php esc_html_e( 'Options used in select, radio and multiple checkboxes. 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. For file fields, this indicates allowed extensions (one per line without dot)', 'contact-forms' ); ?></p>
412 </div>
413
414 <div class="form-field" id="field-section-date">
415 <strong><?php esc_html_e( 'Settings for date fields', 'contact-forms' ); ?></strong>
416 <div class="form-field">
417 <label for="form-field-default-date-value"><?php esc_html_e( 'Default value', 'contact-forms' ); ?>:</label>
418 <input type="date" name="form-field-default-date-value" id="form-field-default-date-value" value="<?php echo esc_attr( $default_form_values['default_date_value'] ); ?>">
419 </div>
420
421 <label for="form-field-min-of-date"><?php esc_html_e( 'Min date', 'contact-forms' ); ?>:</label>
422 <input type="date" id="form-field-min-of-date" name="form-field-min-of-date" value="<?php echo esc_attr( $default_form_values['min_date'] ); ?>">
423
424 <label for="form-field-max-of-date"><?php esc_html_e( 'Max date', 'contact-forms' ); ?>:</label>
425 <input type="date" id="form-field-max-of-date" name="form-field-max-of-date" value="<?php echo esc_attr( $default_form_values['max_date'] ); ?>">
426
427 </div>
428
429 <div class="form-field" id="field-section-custom-required">
430 <label for="form-field-custom-required-message"><?php esc_html_e( 'Custom required message', 'contact-forms' ); ?></label>
431 <input name="form-field-custom-required-message" id="form-field-custom-required-message" type="text" value="<?php echo esc_attr( $default_form_values['custom_required_message'] ); ?>" />
432 <?php // translators: %s is the field name/label placeholder ?>
433 <p><?php esc_html_e( 'Overrides the default "required" error message for this field. Use %s for the field name. Leave blank to use the default translated message.', 'contact-forms' ); ?></p>
434 </div>
435
436 <div class="form-field" id="field-section-custom-format">
437 <label for="form-field-custom-format-message"><?php esc_html_e( 'Custom format message', 'contact-forms' ); ?></label>
438 <input name="form-field-custom-format-message" id="form-field-custom-format-message" type="text" value="<?php echo esc_attr( $default_form_values['custom_format_message'] ); ?>" />
439 <?php // translators: %s is the field name/label placeholder ?>
440 <p><?php esc_html_e( 'Overrides the default format error message for email and telephone fields. Use %s for the field name. Leave blank to use the default translated message.', 'contact-forms' ); ?></p>
441 </div>
442
443 <?php
444 if ( $adding ) {
445 submit_button( __( 'Add new field', 'contact-forms' ), 'button' );
446 } else {
447 submit_button( __( 'Save changes', 'contact-forms' ), 'button' );
448 submit_button( __( 'Delete field', 'contact-forms' ), 'button', 'delete-field' );
449 }
450 ?>
451 </form>
452 </div>
453
454 </div>
455 </div><!-- /col-left -->
456
457 </div><!-- /col-container -->
458 </div><!-- /wrap -->
459 <?php
460 }
461