PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 3.3.9
Pods – Custom Content Types and Fields v3.3.9
trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / includes / forms.php
pods / includes Last commit date
compatibility 3 months ago access.php 4 months ago classes.php 4 months ago compatibility.php 4 months ago data.php 4 months ago forms.php 4 months ago general.php 1 month ago media.php 4 months ago
forms.php
596 lines
1 <?php
2
3 // Don't load directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 die( '-1' );
6 }
7
8 /**
9 * @package Pods\Global\Functions\Forms
10 */
11
12 use Pods\Whatsit;
13 use Pods\Whatsit\Field;
14 use Pods\Whatsit\Pod;
15 use Pods\Whatsit\Store;
16 use Pods\Permissions;
17
18 /**
19 * Enqueue a script in a way that is compatible with enqueueing before the asset was registered.
20 *
21 * @since 2.8.6
22 *
23 * @param string $handle Name of the script. Should be unique.
24 * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory.
25 * Default empty.
26 * @param string[] $deps Optional. An array of registered script handles this script depends on. Default empty array.
27 * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL
28 * as a query string for cache busting purposes. If version is set to false, a version
29 * number is automatically added equal to current installed WordPress version.
30 * If set to null, no version is added.
31 * @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
32 * Default 'false'.
33 */
34 function pods_form_enqueue_script( $handle, $src = '', $deps = [], $ver = false, $in_footer = false ) {
35 // Use dynamic arguments to support future versions of wp_enqueue_script.
36 $args = func_get_args();
37
38 // Check that the script is already registered, or we are registering it when enqueueing.
39 if ( ! empty( $src ) || wp_script_is( $handle, 'registered' ) ) {
40 call_user_func_array( 'wp_enqueue_script', $args );
41
42 return;
43 }
44
45 // The script was enqueued before the enqueue scripts action was called.
46 add_action( 'pods_after_enqueue_scripts', static function() use ( $args ) {
47 call_user_func_array( 'wp_enqueue_script', $args );
48 } );
49 }
50
51 /**
52 * Enqueue a style in a way that is compatible with enqueueing before the asset was registered.
53 *
54 * @since 2.8.6
55 *
56 * @param string $handle Name of the stylesheet. Should be unique.
57 * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory.
58 * Default empty.
59 * @param string[] $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array.
60 * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL
61 * as a query string for cache busting purposes. If version is set to false, a version
62 * number is automatically added equal to current installed WordPress version.
63 * If set to null, no version is added.
64 * @param string $media Optional. The media for which this stylesheet has been defined.
65 * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like
66 * '(orientation: portrait)' and '(max-width: 640px)'.
67 */
68 function pods_form_enqueue_style( $handle, $src = '', $deps = array(), $ver = false, $media = 'all' ) {
69 // Use dynamic arguments to support future versions of wp_enqueue_script.
70 $args = func_get_args();
71
72 // Check that the style is already registered, or we are registering it when enqueueing.
73 if ( ! empty( $src ) || wp_style_is( $handle, 'registered' ) ) {
74 call_user_func_array( 'wp_enqueue_style', $args );
75
76 return;
77 }
78
79 // The style was enqueued before the enqueue scripts action was called.
80 add_action( 'pods_after_enqueue_scripts', static function() use ( $args ) {
81 call_user_func_array( 'wp_enqueue_style', $args );
82 } );
83 }
84
85 /**
86 * Render form fields outside of the <form> context.
87 *
88 * @since 2.8.0
89 *
90 * @param string $name The object name.
91 * @param int|string $object_id The object ID.
92 * @param array $options The customization options.
93 */
94 function pods_form_render_fields( $name, $object_id, array $options = [] ) {
95 $defaults = [
96 'section_field' => null,
97 'section' => null,
98 'separator' => 'before',
99 'wrapper' => false,
100 'wrapper_class' => null,
101 'container_class' => null,
102 'heading' => 'h2',
103 'heading_class' => null,
104 'heading_sub_container' => null,
105 'heading_sub_container_class' => null,
106 'separated_heading' => null,
107 'render' => 'table',
108 ];
109
110 $options = array_merge( $defaults, $options );
111
112 $pod = pods( $name, $object_id, true );
113
114 if ( ! $pod ) {
115 return;
116 }
117
118 // Return groups.
119 $options['return_type'] = 'group';
120
121 $wrapper_classes = [
122 'pods-form-wrapper',
123 'pods-form-wrapper--pod--' . $name,
124 ];
125
126 if ( $options['wrapper_class'] ) {
127 if ( ! is_array( $options['wrapper_class'] ) ) {
128 $options['wrapper_class'] = explode( ' ', $options['wrapper_class'] );
129 }
130
131 foreach ( $options['wrapper_class'] as $wrapper_class ) {
132 $wrapper_classes[] = $wrapper_class;
133 }
134 }
135
136 $wrapper_classes = array_map( 'sanitize_html_class', $wrapper_classes );
137 $wrapper_classes = implode( ' ', $wrapper_classes );
138
139 // Get groups/fields and render them.
140 $groups = pods_form_get_visible_objects( $pod, $options );
141
142 foreach ( $groups as $group ) {
143 $fields = $group->get_fields();
144
145 /**
146 * Allow hooking into before the form field output for a group is rendered.
147 *
148 * @since 2.8.0
149 *
150 * @param Whatsit\Group $group The Group object.
151 */
152 do_action( 'pods_form_render_fields_group_pre', $group );
153
154 $is_table_separated_render = 'table-separated' === $options['render'];
155 $is_table_render = 'table' === $options['render'] || $is_table_separated_render;
156 $is_table_rows_render = 'table-rows' === $options['render'];
157 $is_div_rows_render = 'div-rows' === $options['render'];
158
159 if ( $is_table_separated_render ) {
160 echo "</table>\n";
161 }
162
163 if ( ! $is_table_rows_render && 'before' === $options['separator'] ) {
164 echo "<hr />\n";
165 }
166
167 if ( $is_table_rows_render ) {
168 printf(
169 '<tr><td colspan="2" class="%s">',
170 esc_attr( $wrapper_classes )
171 );
172 } elseif ( $is_div_rows_render && $options['wrapper'] ) {
173 printf(
174 '<div class="%s">',
175 esc_attr( $wrapper_classes )
176 );
177 }
178
179 if ( $options['heading'] ) {
180 $heading_classes = [
181 'pods-form-heading',
182 'pods-form-heading--pod-' . $name,
183 'pods-form-heading--group',
184 'pods-form-heading--group-' . $group['name'],
185 ];
186
187 if ( $options['heading_class'] ) {
188 if ( ! is_array( $options['heading_class'] ) ) {
189 $options['heading_class'] = explode( ' ', $options['heading_class'] );
190 }
191
192 foreach ( $options['heading_class'] as $heading_class ) {
193 $heading_classes[] = $heading_class;
194 }
195 }
196
197 $heading_classes = array_map( 'sanitize_html_class', $heading_classes );
198 $heading_classes = implode( ' ', $heading_classes );
199
200 $heading_text = wp_kses_post( $group['label'] );
201
202 if ( $options['heading_sub_container'] ) {
203 $heading_sub_container_classes = [];
204
205 if ( $options['heading_sub_container_class'] ) {
206 if ( ! is_array( $options['heading_sub_container_class'] ) ) {
207 $options['heading_sub_container_class'] = explode( ' ', $options['heading_sub_container_class'] );
208 }
209
210 foreach ( $options['heading_sub_container_class'] as $heading_sub_container_class ) {
211 $heading_sub_container_classes[] = $heading_sub_container_class;
212 }
213 }
214
215 $heading_sub_container_extra_html = '';
216
217 if ( ! empty( $heading_sub_container_classes ) ) {
218 $heading_sub_container_classes = array_map( 'sanitize_html_class', $heading_sub_container_classes );
219 $heading_sub_container_classes = implode( ' ', $heading_sub_container_classes );
220
221 $heading_sub_container_extra_html = sprintf(
222 ' class="%s"',
223 esc_attr( $heading_sub_container_classes )
224 );
225 }
226
227 $heading_text = sprintf(
228 '<%1$s%2$s>%3$s</%1$s>' . "\n",
229 esc_html( $options['heading_sub_container'] ),
230 $heading_sub_container_extra_html,
231 $heading_text
232 );
233 }
234
235 printf(
236 '<%1$s class="%2$s">%3$s</%1$s>' . "\n",
237 esc_html( $options['heading'] ),
238 esc_attr( $heading_classes ),
239 wp_kses_post( $heading_text )
240 );
241 }
242
243 if ( $is_table_rows_render ) {
244 echo '</td></tr>';
245 }
246
247 $id = $object_id;
248
249 $container_classes = [
250 'pods-form',
251 'pods-form-container',
252 'pods-form-container--pod--' . $name,
253 'pods-form-container--group',
254 'pods-form-container--group--' . $group['name'],
255 ];
256
257 if ( $options['container_class'] ) {
258 if ( ! is_array( $options['container_class'] ) ) {
259 $options['container_class'] = explode( ' ', $options['container_class'] );
260 }
261
262 foreach ( $options['container_class'] as $container_class ) {
263 $container_classes[] = $container_class;
264 }
265 }
266
267 $container_classes = array_map( 'sanitize_html_class', $container_classes );
268 $container_classes = implode( ' ', $container_classes );
269
270 if ( $is_table_render || $is_table_rows_render ) {
271 if ( $is_table_render ) {
272 echo '<table class="form-table ' . esc_attr( $container_classes ) . '">' . "\n";
273 }
274
275 $field_prefix = 'pods_meta_';
276 $field_row_classes = 'pods-meta';
277
278 pods_view( PODS_DIR . 'ui/forms/table-rows.php', compact( array_keys( get_defined_vars() ) ) );
279
280 if ( $is_table_render ) {
281 echo "</table>\n";
282 }
283 } elseif ( 'div-rows' === $options['render'] ) {
284 echo '<div class="' . esc_attr( $container_classes ) . '">' . "\n";
285
286 $field_prefix = 'pods_meta_';
287 $field_row_classes = 'pods-meta';
288
289 pods_view( PODS_DIR . 'ui/forms/div-rows.php', compact( array_keys( get_defined_vars() ) ) );
290
291 echo "</div>\n";
292 }
293
294 if ( $is_table_separated_render ) {
295 if ( $options['heading'] && $options['separated_heading'] ) {
296 printf( '<%1$s>%2$s</%1$s>' . "\n", esc_html( $options['heading'] ), wp_kses_post( $options['separated_heading'] ) );
297 }
298
299 echo '<table class="form-table">' . "\n";
300 } elseif ( $is_div_rows_render && $options['wrapper'] ) {
301 echo '</div>';
302 }
303
304 if ( ! $is_table_rows_render && 'after' === $options['separator'] ) {
305 echo "<hr />\n";
306 }
307
308 /**
309 * Allow hooking into after the form field output for a group is rendered.
310 *
311 * @since 2.8.0
312 *
313 * @param Whatsit\Group $group The Group object.
314 */
315 do_action( 'pods_form_render_fields_group_post', $group );
316 }
317 }
318
319 /**
320 * Get the list of Groups or Fields that are able to be shown.
321 *
322 * @since 2.8.0
323 *
324 * @param Pods $pod The Pods object.
325 * @param array $options The customization options.
326 *
327 * @return Whatsit\Group[]|Whatsit\Field[] List of Groups or Fields that are able to be shown.
328 */
329 function pods_form_get_visible_objects( $pod, array $options = [] ) {
330 $defaults = [
331 'section_field' => null,
332 'section' => null,
333 'return_type' => 'group',
334 ];
335
336 $options = array_merge( $defaults, $options );
337
338 $visible_groups = [];
339 $visible_fields = [];
340
341 $return_fields = 'field' === $options['return_type'];
342
343 // Get groups/fields and render them.
344 $groups = $pod->pod_data->get_groups();
345
346 foreach ( $groups as $group ) {
347 // Skip if the section does not match.
348 if (
349 $options['section']
350 && $options['section_field']
351 && (
352 'any' === $options['section']
353 || ! in_array( $options['section'], (array) $group[ $options['section_field'] ], true )
354 )
355 ) {
356 continue;
357 }
358
359 $fields = $group->get_fields();
360
361 if ( empty( $fields ) ) {
362 continue;
363 }
364
365 if ( ! pods_permission( $group ) ) {
366 continue;
367 }
368
369 $field_found = false;
370
371 foreach ( $fields as $field ) {
372 if ( ! pods_permission( $field ) ) {
373 continue;
374 }
375
376 if ( pods_v( 'hidden', $field, false ) ) {
377 continue;
378 }
379
380 if ( $return_fields ) {
381 $visible_fields[ $field['name'] ] = $field;
382
383 continue;
384 }
385
386 $field_found = true;
387
388 break;
389 }
390
391 if ( ! $field_found ) {
392 continue;
393 }
394
395 $visible_groups[ $group['name'] ] = $group;
396 }
397
398 if ( $return_fields ) {
399 return $visible_fields;
400 }
401
402 return $visible_groups;
403 }
404
405 /**
406 * Validate the submitted fields from the form.
407 *
408 * @since 2.8.0
409 *
410 * @param string $name The object name.
411 * @param int|string|null $object_id The object ID.
412 * @param array $options The customization options.
413 *
414 * @return true|WP_Error[]|null True if the fields validate, a list of WP_Error objects with validation errors, or null if Pod does not exist.
415 */
416 function pods_form_validate_submitted_fields( $name, $object_id = null, array $options = [] ) {
417 $pod = pods( $name, $object_id, true );
418
419 if ( ! $pod ) {
420 return null;
421 }
422
423 // Get the fields.
424 $options['return_type'] = 'field';
425
426 // Get fields and save them.
427 $fields = pods_form_get_visible_objects( $pod, $options );
428
429 $api = pods_api();
430
431 // Enforce WP_Error objects for validation errors.
432 $api->display_errors = 'wp_error';
433
434 $errors = [];
435
436 foreach ( $fields as $field ) {
437 $field_name = $field['name'];
438
439 $value = pods_form_get_submitted_field_value( $field_name );
440
441 /** @var array|bool|WP_Error $field_is_valid */
442 $field_is_valid = $api->handle_field_validation( $value, $field_name, [], $fields, $pod );
443
444 if ( is_wp_error( $field_is_valid ) ) {
445 $errors[] = $field_is_valid;
446 }
447 }
448
449 // Check for validation errors.
450 if ( ! empty( $errors ) ) {
451 return $errors;
452 }
453
454 // Fields are valid.
455 return true;
456 }
457
458 /**
459 * Save the submitted fields from the form.
460 *
461 * @since 2.8.0
462 *
463 * @param string $name The object name.
464 * @param int|string $object_id The object ID.
465 * @param bool $is_new_item Whether this is a new item being saved.
466 * @param array $options The customization options.
467 *
468 * @return int|null The saved item or null if the pod does not exist.
469 */
470 function pods_form_save_submitted_fields( $name, $object_id, $is_new_item = false, array $options = [] ) {
471 $pod = pods( $name, $object_id, true );
472
473 if ( ! $pod ) {
474 return null;
475 }
476
477 // Get the submitted field values.
478 $data = pods_form_get_submitted_field_values( $name, $options );
479
480 return $pod->save( $data, null, null, [
481 'is_new_item' => $is_new_item,
482 'podsmeta' => true,
483 ] );
484 }
485
486 /**
487 * Get the submitted field values from the form.
488 *
489 * @since 2.8.0
490 *
491 * @param string $name The object name.
492 * @param array $options The customization options.
493 *
494 * @return array List of submitted field values and their values.
495 */
496 function pods_form_get_submitted_field_values( $name, array $options = [] ) {
497 // Get the submitted fields.
498 $fields = pods_form_get_submitted_fields( $name, $options );
499 $fields = array_keys( $fields );
500
501 $data = [];
502
503 foreach ( $fields as $field_name ) {
504 $data[ $field_name ] = pods_form_get_submitted_field_value( $field_name );
505 }
506
507 return $data;
508 }
509
510 /**
511 * Get the submitted field value for a field.
512 *
513 * @since 2.8.0
514 *
515 * @param string|array|Field $field The field name or object.
516 * @param string $method The method to get the value from (default: post).
517 *
518 * @return mixed The submitted field value for a field.
519 */
520 function pods_form_get_submitted_field_value( $field, $method = 'post' ) {
521 $field_name = $field;
522
523 if ( $field instanceof Field ) {
524 $field_name = $field->get_name();
525 } elseif ( is_array( $field ) ) {
526 $field_name = $field['name'];
527 } elseif ( ! is_string( $field ) ) {
528 return '';
529 }
530
531 return pods_v( 'pods_meta_' . $field_name, $method, '' );
532 }
533
534
535 /**
536 * Get the submitted fields from the form.
537 *
538 * @since 2.8.0
539 *
540 * @param string $name The object name.
541 * @param array $options The customization options.
542 *
543 * @return array List of submitted fields and their values.
544 */
545 function pods_form_get_submitted_fields( $name, array $options = [] ) {
546 $pod = pods( $name, null, true );
547
548 if ( ! $pod ) {
549 return [];
550 }
551
552 // Get the fields.
553 $options['return_type'] = 'field';
554
555 // Get fields and save them.
556 return pods_form_get_visible_objects( $pod, $options );
557 }
558
559 /**
560 * Make a field into a hidden field.
561 *
562 * @since 3.3.5
563 *
564 * @param array|Field $field The field object.
565 *
566 * @return array|Field The field object.
567 */
568 function pods_form_field_make_hidden( $field ) {
569 if ( $field instanceof Field ) {
570 $field = clone $field;
571 }
572
573 $field['type'] = 'hidden';
574
575 return $field;
576 }
577
578 /**
579 * Make a field into a readonly field.
580 *
581 * @since 3.3.5
582 *
583 * @param array|Field $field The field object.
584 *
585 * @return array|Field The field object.
586 */
587 function pods_form_field_make_readonly( $field ) {
588 if ( $field instanceof Field ) {
589 $field = clone $field;
590 }
591
592 $field['readonly'] = true;
593
594 return $field;
595 }
596