PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 3.3.6
Pods – Custom Content Types and Fields v3.3.6
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 / classes / fields / pick.php
pods / classes / fields Last commit date
avatar.php 4 months ago boolean.php 4 months ago code.php 4 months ago color.php 4 months ago comment.php 4 months ago currency.php 4 months ago date.php 4 months ago datetime.php 4 months ago email.php 4 months ago file.php 4 months ago heading.php 4 months ago html.php 4 months ago link.php 4 months ago number.php 4 months ago oembed.php 4 months ago paragraph.php 4 months ago password.php 4 months ago phone.php 4 months ago pick.php 4 months ago slug.php 4 months ago taxonomy.php 4 months ago text.php 4 months ago time.php 4 months ago website.php 4 months ago wysiwyg.php 4 months ago
pick.php
4383 lines
1 <?php
2
3 // Don't load directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 die( '-1' );
6 }
7
8 use Pods\Static_Cache;
9 use Pods\Whatsit\Pod;
10 use Pods\Whatsit\Field;
11 use Pods\Whatsit\Object_Field;
12 use Pods\API\Whatsit\Value_Field;
13 use Pods\Whatsit\Store;
14
15 /**
16 * @package Pods\Fields
17 */
18 class PodsField_Pick extends PodsField {
19
20 /**
21 * {@inheritdoc}
22 */
23 public static $group = 'Relationships / Media';
24
25 /**
26 * {@inheritdoc}
27 */
28 public static $type = 'pick';
29
30 /**
31 * {@inheritdoc}
32 */
33 public static $label = 'Relationship';
34
35 /**
36 * {@inheritdoc}
37 */
38 protected static $api = false;
39
40 /**
41 * Available Related Objects.
42 *
43 * @var array
44 * @since 2.3.0
45 */
46 public static $related_objects = [];
47
48 /**
49 * Custom Related Objects
50 *
51 * @var array
52 * @since 2.3.0
53 */
54 public static $custom_related_objects = [];
55
56 /**
57 * Data used during validate / save to avoid extra queries.
58 *
59 * @var array
60 * @since 2.3.0
61 */
62 public static $related_data = [];
63
64 /**
65 * Data used during input method (mainly for autocomplete).
66 *
67 * @var array
68 * @since 2.3.0
69 */
70 public static $field_data = [];
71
72 /**
73 * Saved array of simple relationship names.
74 *
75 * @var array
76 * @since 2.5.0
77 */
78 private static $names_simple = null;
79
80 /**
81 * Saved array of relationship names
82 *
83 * @var array
84 * @since 2.5.0
85 */
86 private static $names_related = null;
87
88 /**
89 * Saved array of bidirectional relationship names
90 *
91 * @var array
92 * @since 2.5.0
93 */
94 private static $names_bidirectional = null;
95
96 /**
97 * {@inheritdoc}
98 */
99 public function setup() {
100
101 static::$group = __( 'Relationships / Media', 'pods' );
102 static::$label = __( 'Relationship', 'pods' );
103 }
104
105 /**
106 * {@inheritdoc}
107 */
108 public function admin_init() {
109
110 // AJAX for Relationship lookups.
111 add_action( 'wp_ajax_pods_relationship', [ $this, 'admin_ajax_relationship' ] );
112 add_action( 'wp_ajax_nopriv_pods_relationship', [ $this, 'admin_ajax_relationship' ] );
113
114 // Handle modal input.
115 add_action( 'pods_meta_box_pre', [ $this, 'admin_modal_input' ] );
116 add_action( 'edit_form_top', [ $this, 'admin_modal_input' ] );
117 add_action( 'show_user_profile', [ $this, 'admin_modal_input' ] );
118 add_action( 'edit_user_profile', [ $this, 'admin_modal_input' ] );
119
120 // Hook into every taxonomy form.
121 $taxonomies = get_taxonomies();
122
123 foreach ( $taxonomies as $taxonomy ) {
124 if ( $taxonomy instanceof WP_Term ) {
125 $taxonomy = $taxonomy->name;
126 }
127
128 add_action( $taxonomy . '_add_form', [ $this, 'admin_modal_input' ] );
129 add_action( $taxonomy . '_edit_form', [ $this, 'admin_modal_input' ] );
130 }
131
132 // Handle modal saving.
133 add_filter( 'redirect_post_location', [ $this, 'admin_modal_bail_post_redirect' ], 10, 2 );
134 add_action( 'load-edit-tags.php', [ $this, 'admin_modal_bail_term_action' ] );
135 add_action( 'load-categories.php', [ $this, 'admin_modal_bail_term_action' ] );
136 add_action( 'load-edit-link-categories.php', [ $this, 'admin_modal_bail_term_action' ] );
137 add_action( 'personal_options_update', [ $this, 'admin_modal_bail_user_action' ] );
138 add_action( 'user_register', [ $this, 'admin_modal_bail_user_action' ] );
139 add_action( 'pods_api_processed_form', [ $this, 'admin_modal_bail_pod' ], 10, 3 );
140
141 }
142
143 /**
144 * {@inheritdoc}
145 */
146 public function options() {
147 // translators: %s: is the Documentation linked text.
148 $fallback_help = __( 'More details on our %s.', 'pods' );
149
150 $fallback_help_link = sprintf(
151 '<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>',
152 esc_url( 'https://docs.pods.io/fields/relationship/' ),
153 __( 'Field Type Documentation', 'pods' )
154 );
155
156 $fallback_help = sprintf( $fallback_help, $fallback_help_link );
157
158 $simple_objects = $this->simple_objects();
159
160 $slow_text = __( '(may be slow for large data sets)', 'pods' );
161
162 $options = [
163 static::$type . '_format_type' => [
164 'label' => __( 'Selection Type', 'pods' ),
165 'help' => $fallback_help,
166 'default' => 'single',
167 'required' => true,
168 'type' => 'pick',
169 'data' => [
170 'single' => __( 'Single Select', 'pods' ),
171 'multi' => __( 'Multiple Select', 'pods' ),
172 ],
173 'pick_format_single' => 'dropdown',
174 'pick_show_select_text' => 0,
175 'dependency' => true,
176 ],
177 static::$type . '_format_single' => [
178 'label' => __( 'Input Type', 'pods' ),
179 'help' => $fallback_help,
180 'depends-on' => [
181 static::$type . '_format_type' => 'single',
182 ],
183 'default' => 'dropdown',
184 'required' => true,
185 'type' => 'pick',
186 'data' => apply_filters( 'pods_form_ui_field_pick_format_single_options', [
187 'dropdown' => __( 'Drop Down', 'pods' ) . ' ' . $slow_text,
188 'radio' => __( 'Radio Buttons', 'pods' ) . ' ' . $slow_text,
189 'autocomplete' => __( 'Autocomplete', 'pods' ),
190 'list' => __( 'List View (single value)', 'pods' ),
191 ] ),
192 'pick_format_single' => 'dropdown',
193 'pick_show_select_text' => 0,
194 'dependency' => true,
195 ],
196 static::$type . '_format_single_help' => [
197 'label' => '',
198 'type' => 'html',
199 'default' => 0,
200 'html_content' => '<p><em>' . esc_html__( 'Please note: When relating to dynamic content with large amounts of data, Drop Down or Radio Buttons can cause the edit screen to load very slowly because it loads all data at once. Consider using the Autocomplete or List View instead.', 'pods' ) . '</em></p>',
201 'dependency' => true,
202 'wildcard-on' => [
203 static::$type . '_format_single' => [
204 '^dropdown$',
205 '^radio$',
206 ],
207 static::$type . '_object' => [
208 '^post_type-(?!(custom_css|customize_changeset)).*$',
209 '^taxonomy-.*$',
210 '^user$',
211 '^pod-.*$',
212 ],
213 ],
214 ],
215 static::$type . '_format_multi' => [
216 'label' => __( 'Input Type', 'pods' ),
217 'help' => $fallback_help,
218 'depends-on' => [
219 static::$type . '_format_type' => 'multi',
220 ],
221 'default' => 'list',
222 'required' => true,
223 'type' => 'pick',
224 'data' => apply_filters( 'pods_form_ui_field_pick_format_multi_options', [
225 'checkbox' => __( 'Checkboxes', 'pods' ) . ' ' . $slow_text,
226 'multiselect' => __( 'Multi Select (basic selection)', 'pods' ) . ' ' . $slow_text,
227 'autocomplete' => __( 'Autocomplete', 'pods' ),
228 'list' => __( 'List View (with reordering)', 'pods' ),
229 ] ),
230 'pick_format_single' => 'dropdown',
231 'pick_show_select_text' => 0,
232 'dependency' => true,
233 ],
234 static::$type . '_format_multi_help' => [
235 'label' => '',
236 'type' => 'html',
237 'default' => 0,
238 'html_content' => '<p><em>' . esc_html__( 'Please note: When relating to dynamic content with large amounts of data, Checkboxes or Multi Select can cause the edit screen to load very slowly because it loads all data at once. Consider using the Autocomplete or List View instead.', 'pods' ) . '</em></p>',
239 'dependency' => true,
240 'wildcard-on' => [
241 static::$type . '_format_multi' => [
242 '^checkbox$',
243 '^multiselect$',
244 ],
245 static::$type . '_object' => [
246 '^post_type-(?!(custom_css|customize_changeset)).*$',
247 '^taxonomy-.*$',
248 '^user$',
249 '^pod-.*$',
250 ],
251 ],
252 ],
253 static::$type . '_display_format_multi' => [
254 'label' => __( 'Display Format', 'pods' ),
255 'help' => __( 'Used as format for front-end display', 'pods' ) . ' ' . $fallback_help,
256 'depends-on' => [
257 static::$type . '_format_type' => 'multi',
258 ],
259 'default' => 'default',
260 'required' => true,
261 'type' => 'pick',
262 'data' => [
263 'default' => __( 'Item 1, Item 2, and Item 3', 'pods' ),
264 'non_serial' => __( 'Item 1, Item 2 and Item 3', 'pods' ),
265 'br' => __( 'One per line (br HTML tags)', 'pods' ),
266 'ul' => __( 'Unordered HTML list', 'pods' ),
267 'ol' => __( 'Ordered HTML list', 'pods' ),
268 'custom' => __( 'Custom separator (without "and")', 'pods' ),
269 ],
270 'pick_format_single' => 'dropdown',
271 'pick_show_select_text' => 0,
272 'dependency' => true,
273 ],
274 static::$type . '_display_format_separator' => [
275 'label' => __( 'Display Format Separator', 'pods' ),
276 'help' => __( 'Used as separator for front-end display. This also turns off the "and" portion of the formatting.', 'pods' ) . ' ' . $fallback_help,
277 'depends-on' => [
278 static::$type . '_display_format_multi' => 'custom',
279 static::$type . '_format_type' => 'multi',
280 ],
281 'default' => ', ',
282 'type' => 'text',
283 ],
284 static::$type . '_allow_add_new' => [
285 'label' => __( 'Allow Add New', 'pods' ),
286 'help' => __( 'Allow new related records to be created in a modal window', 'pods' ) . ' ' . $fallback_help,
287 'wildcard-on' => [
288 static::$type . '_object' => [
289 '^post_type-(?!(custom_css|customize_changeset)).*$',
290 //'^taxonomy-.*$', @todo We need to finish adding support for add new on term form.
291 '^user$',
292 '^pod-.*$',
293 ],
294 ],
295 'type' => 'boolean',
296 'default' => 1,
297 ],
298 static::$type . '_add_new_label' => [
299 'label' => __( 'Add New Label', 'pods' ),
300 'placeholder' => __( 'Add New', 'pods' ),
301 'default' => '',
302 'type' => 'text',
303 'depends-on' => [ static::$type . '_allow_add_new' => true ],
304 ],
305 static::$type . '_taggable' => [
306 'label' => __( 'Taggable', 'pods' ),
307 'help' => __( 'Allow new values to be inserted when using an Autocomplete field', 'pods' ) . ' ' . $fallback_help,
308 'depends-on-any' => [
309 static::$type . '_format_single' => 'autocomplete',
310 static::$type . '_format_multi' => 'autocomplete',
311 ],
312 'excludes-on' => [
313 static::$type . '_object' => array_merge( [
314 'site',
315 'network',
316 ], $simple_objects ),
317 static::$type . '_allow_add_new' => false,
318 ],
319 'type' => 'boolean',
320 'default' => 0,
321 ],
322 static::$type . '_show_icon' => [
323 'label' => __( 'Show Icons', 'pods' ),
324 'help' => $fallback_help,
325 'depends-on-any' => [
326 static::$type . '_format_single' => 'list',
327 static::$type . '_format_multi' => 'list',
328 ],
329 'excludes-on' => [
330 static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
331 ],
332 'type' => 'boolean',
333 'default' => 1,
334 ],
335 static::$type . '_show_edit_link' => [
336 'label' => __( 'Show Edit Links', 'pods' ),
337 'help' => $fallback_help,
338 'depends-on-any' => [
339 static::$type . '_format_single' => 'list',
340 static::$type . '_format_multi' => 'list',
341 ],
342 'excludes-on' => [
343 static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
344 ],
345 'type' => 'boolean',
346 'default' => 1,
347 ],
348 static::$type . '_show_view_link' => [
349 'label' => __( 'Show View Links', 'pods' ),
350 'help' => $fallback_help,
351 'depends-on-any' => [
352 static::$type . '_format_single' => 'list',
353 static::$type . '_format_multi' => 'list',
354 ],
355 'excludes-on' => [
356 static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
357 ],
358 'type' => 'boolean',
359 'default' => 1,
360 ],
361 static::$type . '_select_text' => [
362 'label' => __( 'Default Select Text', 'pods' ),
363 'help' => __( 'This is the text used for the default "no selection" dropdown item. If left empty, it will default to "-- Select One --"', 'pods' ) . ' ' . $fallback_help,
364 'depends-on' => [
365 static::$type . '_format_type' => 'single',
366 static::$type . '_format_single' => 'dropdown',
367 ],
368 'default' => '',
369 'text_placeholder' => __( '-- Select One --', 'pods' ),
370 'type' => 'text',
371 ],
372 static::$type . '_limit' => [
373 'label' => __( 'Selection Limit', 'pods' ),
374 'help' => __( 'Default is "0" for no limit, but you can enter 1 or more to limit the number of items that can be selected.', 'pods' ) . ' ' . $fallback_help,
375 'depends-on' => [
376 static::$type . '_format_type' => 'multi',
377 ],
378 'default' => 0,
379 'type' => 'number',
380 ],
381 static::$type . '_table_id' => [
382 'label' => __( 'Table ID Column', 'pods' ),
383 'help' => __( 'You must provide the ID column name for the table, this will be used to keep track of the relationship', 'pods' ) . ' ' . $fallback_help,
384 'depends-on' => [
385 static::$type . '_object' => 'table',
386 ],
387 'required' => 1,
388 'default' => '',
389 'type' => 'text',
390 ],
391 static::$type . '_table_index' => [
392 'label' => __( 'Table Index Column', 'pods' ),
393 'help' => __( 'You must provide the index column name for the table, this may optionally also be the ID column name', 'pods' ) . ' ' . $fallback_help,
394 'depends-on' => [
395 static::$type . '_object' => 'table',
396 ],
397 'required' => 1,
398 'default' => '',
399 'type' => 'text',
400 ],
401 static::$type . '_display' => [
402 'label' => __( 'Display Field in Selection List', 'pods' ),
403 'help' => __( 'Provide the name of a field on the related object to reference, example: {@post_title}', 'pods' ) . ' ' . $fallback_help,
404 'excludes-on' => [
405 static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
406 ],
407 'default' => '',
408 'type' => 'text',
409 ],
410 static::$type . '_user_role' => [
411 'label' => __( 'Limit list by Role(s)', 'pods' ),
412 'help' => __( 'You can choose to limit Users available for selection by specific role(s).', 'pods' ) . ' ' . $fallback_help,
413 'default' => '',
414 'type' => 'pick',
415 'pick_object' => 'role',
416 'pick_format_type' => 'multi',
417 'pick_format_multi' => 'autocomplete',
418 'depends-on' => [
419 static::$type . '_object' => 'user',
420 ],
421 ],
422 static::$type . '_where' => [
423 'label' => __( 'Customized <em>WHERE</em>', 'pods' ),
424 'help' => $fallback_help,
425 'excludes-on' => [
426 static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
427 ],
428 'default' => '',
429 'type' => 'text',
430 ],
431 static::$type . '_orderby' => [
432 'label' => __( 'Customized <em>ORDER BY</em>', 'pods' ),
433 'help' => $fallback_help,
434 'excludes-on' => [
435 static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
436 ],
437 'default' => '',
438 'type' => 'text',
439 ],
440 static::$type . '_groupby' => [
441 'label' => __( 'Customized <em>GROUP BY</em>', 'pods' ),
442 'help' => $fallback_help,
443 'excludes-on' => [
444 static::$type . '_object' => array_merge( [ 'site', 'network' ], $simple_objects ),
445 ],
446 'default' => '',
447 'type' => 'text',
448 ],
449 static::$type . '_sync_taxonomy' => [
450 'label' => __( 'Sync associated taxonomy with this relationship', 'pods' ),
451 'help' => __( 'This will automatically sync the associated taxonomy terms with the value of this relationship if this field is on a Pod that is a Post Type. If the associated taxonomy terms are different, they will be overridden on save.', 'pods' ),
452 'wildcard-on' => [
453 static::$type . '_object' => [
454 '^taxonomy-.*$',
455 ],
456 ],
457 'type' => 'boolean',
458 'default' => 0,
459 ],
460 static::$type . '_sync_taxonomy_hide_taxonomy_ui' => [
461 'label' => __( 'Hide the associated taxonomy UI from the Editor', 'pods' ),
462 'help' => __( 'This will hide the taxonomy meta box from the Classic Editor and disable the taxonomy panel in the Block Editor.', 'pods' ),
463 'depends-on' => [
464 static::$type . '_sync_taxonomy' => true,
465 ],
466 'wildcard-on' => [
467 static::$type . '_object' => [
468 '^taxonomy-.*$',
469 ],
470 ],
471 'type' => 'boolean',
472 'default' => 0,
473 ],
474 ];
475
476 $post_type_pick_objects = [];
477
478 foreach ( get_post_types( '', 'names' ) as $post_type ) {
479 $post_type_pick_objects[] = 'post_type-' . $post_type;
480 }
481
482 $options[ static::$type . '_post_status' ] = [
483 'label' => __( 'Limit list by Post Status', 'pods' ),
484 'help' => __( 'You can choose to limit Posts available for selection by one or more specific post status.', 'pods' ),
485 'type' => 'pick',
486 'pick_object' => 'post-status-with-any',
487 'pick_format_type' => 'multi',
488 'default' => 'publish',
489 'depends-on' => [
490 static::$type . '_object' => $post_type_pick_objects,
491 ],
492 ];
493
494 $options[ static::$type . '_post_author' ] = [
495 'label' => __( 'Limit list to the same Post Author', 'pods' ),
496 'help' => __( 'You can choose to limit Posts available for selection to those created by the same Post Author. This only works if this pod is a Post Type and this field is related to a Post Type.', 'pods' ),
497 'type' => 'boolean',
498 'default' => 0,
499 'depends-on' => [
500 // @todo Support being able to depend on the current pod type like _pod_type => post_type or something.
501 static::$type . '_object' => $post_type_pick_objects,
502 ],
503 ];
504
505 return $options;
506
507 }
508
509 /**
510 * {@inheritdoc}
511 */
512 public function prepare( $options = null ) {
513 $format = static::$prepare;
514
515 // Maybe use number format for storage if not a simple relationship and limit is one.
516 if ( $options instanceof Field && ! $options->is_simple_relationship() && 1 === $options->get_limit() ) {
517 $format = '%d';
518 }
519
520 return $format;
521 }
522
523 /**
524 * Register a related object.
525 *
526 * @param string $name Object name.
527 * @param string $label Object label.
528 * @param array $options Object options.
529 *
530 * @return array|boolean Object array or false if unsuccessful
531 * @since 2.3.0
532 */
533 public function register_related_object( $name, $label, $options = null ) {
534
535 if ( empty( $name ) || empty( $label ) ) {
536 return false;
537 }
538
539 $related_object = [
540 'label' => $label,
541 'group' => 'Custom Relationships',
542 'simple' => true,
543 'bidirectional' => false,
544 'data' => [],
545 'data_callback' => null,
546 ];
547
548 $related_object = array_merge( $related_object, $options );
549
550 if ( $related_object['data_callback'] instanceof Closure ) {
551 return pods_error( 'Pods does not support closures for data callbacks' );
552 }
553
554 self::$custom_related_objects[ $name ] = $related_object;
555
556 return true;
557
558 }
559
560 /**
561 * Setup related objects.
562 *
563 * @param boolean $force Whether to force refresh of related objects.
564 *
565 * @return bool True when data has been loaded
566 * @since 2.3.0
567 */
568 public function setup_related_objects( $force = false ) {
569
570 $new_data_loaded = false;
571
572 if ( ! $force && empty( self::$related_objects ) ) {
573 // Only load transient if we aren't forcing a refresh.
574 self::$related_objects = pods_transient_get( 'pods_related_objects' );
575
576 if ( false !== self::$related_objects ) {
577 $new_data_loaded = true;
578 }
579 } elseif ( $force ) {
580 // If we are rebuilding, make sure we start with a clean slate.
581 self::$related_objects = [];
582 }
583
584 if ( ! is_array( self::$related_objects ) ) {
585 self::$related_objects = [];
586 }
587
588 if ( empty( self::$related_objects ) ) {
589 // Do a complete build of related_objects.
590 $new_data_loaded = true;
591
592 // Custom simple relationship lists.
593 self::$related_objects['custom-simple'] = [
594 'label' => __( 'Simple (custom defined list)', 'pods' ),
595 'group' => __( 'Custom', 'pods' ),
596 'simple' => true,
597 ];
598
599 // Pods options.
600 $pod_options = [];
601
602 // Include PodsMeta if not already included.
603 pods_meta();
604
605 // Advanced Content Types for relationships.
606 $_pods = PodsMeta::$advanced_content_types;
607
608 foreach ( $_pods as $pod ) {
609 $pod_options[ $pod['name'] ] = $pod['label'] . ' (' . $pod['name'] . ')';
610 }
611
612 /**
613 * Allow filtering the list of Pods to show in the list of relationship objects.
614 *
615 * @since 2.8.0
616 *
617 * @param array $pod_options List of Pods to show in the list of relationship objects.
618 */
619 $pod_options = apply_filters( 'pods_field_pick_setup_related_objects_pods', $pod_options );
620
621 asort( $pod_options );
622
623 foreach ( $pod_options as $pod => $label ) {
624 self::$related_objects[ 'pod-' . $pod ] = [
625 'label' => $label,
626 'group' => __( 'Advanced Content Types', 'pods' ),
627 'bidirectional' => true,
628 ];
629 }
630
631 /**
632 * Prevent ability to extend core Pods content types.
633 *
634 * @param bool $ignore_internal Default is true, when set to false Pods internal content types can not be extended.
635 *
636 * @since 2.3.19
637 */
638 $ignore_internal = apply_filters( 'pods_pick_ignore_internal', true );
639
640 $pods_meta = pods_meta();
641
642 // Public Post Types for relationships.
643 $post_types = get_post_types( [ 'public' => true ] );
644 asort( $post_types );
645
646 foreach ( $post_types as $post_type => $label ) {
647 if (
648 empty( $post_type )
649 || 'attachment' === $post_type
650 || (
651 $pods_meta
652 && ! $pods_meta->is_type_covered( 'post_type', $post_type )
653 )
654 ) {
655 unset( $post_types[ $post_type ] );
656
657 continue;
658 } elseif ( $ignore_internal && 0 === strpos( $post_type, '_pods_' ) ) {
659 unset( $post_types[ $post_type ] );
660
661 continue;
662 }
663
664 $post_type = get_post_type_object( $post_type );
665
666 self::$related_objects[ 'post_type-' . $post_type->name ] = [
667 'label' => $post_type->label . ' (' . $post_type->name . ')',
668 'group' => __( 'Post Types', 'pods' ),
669 'bidirectional' => true,
670 ];
671 }
672
673 // Post Types for relationships.
674 $post_types = get_post_types( [ 'public' => false ] );
675 asort( $post_types );
676
677 foreach ( $post_types as $post_type => $label ) {
678 if (
679 empty( $post_type )
680 || 'attachment' === $post_type
681 || (
682 $pods_meta
683 && ! $pods_meta->is_type_covered( 'post_type', $post_type )
684 )
685 ) {
686 unset( $post_types[ $post_type ] );
687
688 continue;
689 } elseif ( $ignore_internal && 0 === strpos( $post_type, '_pods_' ) ) {
690 unset( $post_types[ $post_type ] );
691
692 continue;
693 }
694
695 $post_type = get_post_type_object( $post_type );
696
697 self::$related_objects[ 'post_type-' . $post_type->name ] = [
698 'label' => $post_type->label . ' (' . $post_type->name . ')',
699 'group' => __( 'Post Types (Private)', 'pods' ),
700 'bidirectional' => true,
701 ];
702 }
703
704 // Taxonomies for relationships.
705 $taxonomies = get_taxonomies();
706 asort( $taxonomies );
707
708 foreach ( $taxonomies as $taxonomy => $label ) {
709 if (
710 empty( $taxonomy )
711 || (
712 $pods_meta
713 && ! $pods_meta->is_type_covered( 'taxonomy', $taxonomy )
714 )
715 ) {
716 unset( $taxonomies[ $taxonomy ] );
717
718 continue;
719 } elseif ( $ignore_internal && 0 === strpos( $taxonomy, '_pods_' ) ) {
720 unset( $taxonomies[ $taxonomy ] );
721
722 continue;
723 }
724
725 $taxonomy = get_taxonomy( $taxonomy );
726
727 self::$related_objects[ 'taxonomy-' . $taxonomy->name ] = [
728 'label' => $taxonomy->label . ' (' . $taxonomy->name . ')',
729 'group' => __( 'Taxonomies', 'pods' ),
730 'bidirectional' => true,
731 ];
732 }//end foreach
733
734 // Other WP Objects for relationships.
735 self::$related_objects['user'] = [
736 'label' => __( 'Users', 'pods' ),
737 'group' => __( 'Other WP Objects', 'pods' ),
738 'bidirectional' => true,
739 ];
740
741 self::$related_objects['role'] = [
742 'label' => __( 'User Roles', 'pods' ),
743 'group' => __( 'Other WP Objects', 'pods' ),
744 'simple' => true,
745 'data_callback' => [ $this, 'data_roles' ],
746 ];
747
748 self::$related_objects['capability'] = [
749 'label' => __( 'User Capabilities', 'pods' ),
750 'group' => __( 'Other WP Objects', 'pods' ),
751 'simple' => true,
752 'data_callback' => [ $this, 'data_capabilities' ],
753 ];
754
755 self::$related_objects['media'] = [
756 'label' => __( 'Media', 'pods' ),
757 'group' => __( 'Other WP Objects', 'pods' ),
758 'bidirectional' => true,
759 ];
760
761 self::$related_objects['comment'] = [
762 'label' => __( 'Comments', 'pods' ),
763 'group' => __( 'Other WP Objects', 'pods' ),
764 'bidirectional' => true,
765 ];
766
767 self::$related_objects['image-size'] = [
768 'label' => __( 'Image Sizes', 'pods' ),
769 'group' => __( 'Other WP Objects', 'pods' ),
770 'simple' => true,
771 'data_callback' => [ $this, 'data_image_sizes' ],
772 ];
773
774 self::$related_objects['nav_menu'] = [
775 'label' => __( 'Navigation Menus', 'pods' ),
776 'group' => __( 'Other WP Objects', 'pods' ),
777 ];
778
779 self::$related_objects['post_format'] = [
780 'label' => __( 'Post Formats', 'pods' ),
781 'group' => __( 'Other WP Objects', 'pods' ),
782 ];
783
784 self::$related_objects['post-status'] = [
785 'label' => __( 'Post Status', 'pods' ),
786 'group' => __( 'Other WP Objects', 'pods' ),
787 'simple' => true,
788 'data_callback' => [ $this, 'data_post_stati' ],
789 ];
790
791 self::$related_objects['post-status-with-any'] = [
792 'label' => __( 'Post Status (with any)', 'pods' ),
793 'group' => __( 'Other WP Objects', 'pods' ),
794 'simple' => true,
795 'data_callback' => [ $this, 'data_post_stati_with_any' ],
796 ];
797
798 self::$related_objects['post-types'] = [
799 'label' => __( 'Post Type Objects', 'pods' ),
800 'group' => __( 'Other WP Objects', 'pods' ),
801 'simple' => true,
802 'data_callback' => [ $this, 'data_post_types' ],
803 ];
804
805 self::$related_objects['taxonomies'] = [
806 'label' => __( 'Taxonomy Objects', 'pods' ),
807 'group' => __( 'Other WP Objects', 'pods' ),
808 'simple' => true,
809 'data_callback' => [ $this, 'data_taxonomies' ],
810 ];
811
812 do_action( 'pods_form_ui_field_pick_related_objects_other' );
813
814 self::$related_objects['country'] = [
815 'label' => __( 'Countries', 'pods' ),
816 'group' => __( 'Predefined Lists', 'pods' ),
817 'simple' => true,
818 'data_callback' => [ $this, 'data_countries' ],
819 ];
820
821 self::$related_objects['us_state'] = [
822 'label' => __( 'US States', 'pods' ),
823 'group' => __( 'Predefined Lists', 'pods' ),
824 'simple' => true,
825 'data_callback' => [ $this, 'data_us_states' ],
826 ];
827
828 self::$related_objects['ca_province'] = [
829 'label' => __( 'CA Provinces', 'pods' ),
830 'group' => __( 'Predefined Lists', 'pods' ),
831 'simple' => true,
832 'data_callback' => [ $this, 'data_ca_provinces' ],
833 ];
834
835 self::$related_objects['days_of_week'] = [
836 'label' => __( 'Calendar - Days of Week', 'pods' ),
837 'group' => __( 'Predefined Lists', 'pods' ),
838 'simple' => true,
839 'data_callback' => [ $this, 'data_days_of_week' ],
840 ];
841
842 self::$related_objects['months_of_year'] = [
843 'label' => __( 'Calendar - Months of Year', 'pods' ),
844 'group' => __( 'Predefined Lists', 'pods' ),
845 'simple' => true,
846 'data_callback' => [ $this, 'data_months_of_year' ],
847 ];
848
849 do_action( 'pods_form_ui_field_pick_related_objects_predefined' );
850
851 if ( did_action( 'init' ) ) {
852 pods_transient_set( 'pods_related_objects', self::$related_objects, WEEK_IN_SECONDS );
853 }
854 }//end if
855
856 /**
857 * Allow custom related objects to be defined
858 */
859 do_action( 'pods_form_ui_field_pick_related_objects_custom' );
860
861 foreach ( self::$custom_related_objects as $object => $related_object ) {
862 if ( ! isset( self::$related_objects[ $object ] ) ) {
863 $new_data_loaded = true;
864
865 self::$related_objects[ $object ] = $related_object;
866 }
867 }
868
869 if ( $new_data_loaded ) {
870 /**
871 * Allow hooking in when new data has been loaded.
872 *
873 * @since 2.8.0
874 */
875 do_action( 'pods_form_ui_field_pick_related_objects_new_data_loaded' );
876 }
877
878 return true;
879
880 }
881
882 /**
883 * Return available related objects
884 *
885 * @param boolean $force Whether to force refresh of related objects.
886 *
887 * @return array Field selection array
888 * @since 2.3.0
889 */
890 public function related_objects( $force = false ) {
891 if ( null !== self::$names_related ) {
892 return self::$names_related;
893 }
894
895 $this->setup_related_objects( $force );
896
897 $related_objects = [];
898
899 foreach ( self::$related_objects as $related_object_name => $related_object ) {
900 if ( ! isset( $related_objects[ $related_object['group'] ] ) ) {
901 $related_objects[ $related_object['group'] ] = [];
902 }
903
904 $related_objects[ $related_object['group'] ][ $related_object_name ] = $related_object['label'];
905 }
906
907 self::$names_related = (array) apply_filters( 'pods_form_ui_field_pick_related_objects', $related_objects );
908
909 return self::$names_related;
910 }
911
912 /**
913 * Return available simple object names
914 *
915 * @return array Simple object names
916 * @since 2.3.0
917 */
918 public function simple_objects() {
919 if ( null !== self::$names_simple ) {
920 return self::$names_simple;
921 }
922
923 $this->setup_related_objects();
924
925 $simple_objects = [];
926
927 foreach ( self::$related_objects as $object => $related_object ) {
928 if ( ! isset( $related_object['simple'] ) || ! $related_object['simple'] ) {
929 continue;
930 }
931
932 $simple_objects[] = $object;
933 }
934
935 self::$names_simple = (array) apply_filters( 'pods_form_ui_field_pick_simple_objects', $simple_objects );
936
937 return self::$names_simple;
938 }
939
940 /**
941 * Return available bidirectional object names
942 *
943 * @return array Bidirectional object names
944 * @since 2.3.4
945 */
946 public function bidirectional_objects() {
947 if ( null !== self::$names_bidirectional ) {
948 return self::$names_bidirectional;
949 }
950
951 $this->setup_related_objects();
952
953 $bidirectional_objects = [];
954
955 foreach ( self::$related_objects as $object => $related_object ) {
956 if ( ! isset( $related_object['bidirectional'] ) || ! $related_object['bidirectional'] ) {
957 continue;
958 }
959
960 $bidirectional_objects[] = $object;
961 }
962
963 self::$names_bidirectional = (array) apply_filters( 'pods_form_ui_field_pick_bidirectional_objects', $bidirectional_objects );
964
965 return self::$names_bidirectional;
966 }
967
968 /**
969 * {@inheritdoc}
970 */
971 public function schema( $options = null ) {
972 $field = pods_config_for_field( $options );
973
974 if ( $field && $field->is_simple_relationship() ) {
975 return 'LONGTEXT';
976 }
977
978 return false;
979 }
980
981 /**
982 * {@inheritdoc}
983 */
984 public function display( $value = null, $name = null, $options = null, $pod = null, $id = null ) {
985
986 $fields = null;
987
988 if ( is_object( $pod ) && isset( $pod->fields ) ) {
989 /**
990 * @var $pod Pods Pods object.
991 */
992 $fields = pods_config_get_all_fields( $pod );
993 } elseif ( is_array( $pod ) && isset( $pod['fields'] ) ) {
994 $fields = pods_config_get_all_fields( $pod );
995 }
996
997 $args = [
998 'field' => $name,
999 'fields' => $fields,
1000 ];
1001
1002 $display_format = pods_v( static::$type . '_display_format_multi', $options, 'default' );
1003
1004 switch ( $display_format ) {
1005 case 'ul':
1006 case 'ol':
1007 $value = '<' . $display_format . '><li>' . implode( '</li><li>', (array) $value ) . '</li></' . $display_format . '>';
1008 break;
1009 case 'br':
1010 $value = implode( '<br />', $value );
1011 break;
1012 case 'non_serial':
1013 $args['serial'] = false;
1014 $value = pods_serial_comma( $value, $args );
1015 break;
1016 case 'custom':
1017 $args['serial'] = false;
1018
1019 $separator = pods_v( static::$type . '_display_format_separator', $options, ', ' );
1020 if ( ! empty( $separator ) ) {
1021 $args['separator'] = $separator;
1022
1023 // Replicate separator behavior.
1024 $args['and'] = $args['separator'];
1025 }
1026 $value = pods_serial_comma( $value, $args );
1027 break;
1028 default:
1029 $value = pods_serial_comma( $value, $args );
1030 break;
1031 }
1032
1033 return $value;
1034 }
1035
1036 /**
1037 * {@inheritdoc}
1038 */
1039 public function input( $name, $value = null, $options = null, $pod = null, $id = null ) {
1040 $options = ( is_array( $options ) || is_object( $options ) ) ? $options : (array) $options;
1041
1042 // Do anything we need to do here with options setup / enforcement.
1043
1044 // Default labels.
1045 if ( empty( $options[ static::$type . '_add_new_label' ] ) ) {
1046 $options[ static::$type . '_add_new_label' ] = __( 'Add New', 'pods' );
1047 }
1048
1049 parent::input( $name, $value, $options, $pod, $id );
1050 }
1051
1052 /**
1053 * {@inheritdoc}
1054 */
1055 public function build_dfv_field_options( $options, $args ) {
1056 // Use field object if it was provided.
1057 if ( isset( $options['_field_object'] ) ) {
1058 $field = $options['_field_object'];
1059
1060 unset( $options['_field_object'] );
1061
1062 $options = pods_config_merge_data( $field, $options );
1063 }
1064
1065 $field_options = $options instanceof \Pods\Whatsit ? $options->export() : $options;
1066
1067 if ( ! isset( $field_options['id'] ) ) {
1068 $field_options['id'] = 0;
1069 }
1070
1071 // Enforce defaults.
1072 $all_options = static::options();
1073
1074 foreach ( $all_options as $option_name => $option ) {
1075 $default = pods_v( 'default', $option, '' );
1076
1077 $field_options[ $option_name ] = pods_v( $option_name, $field_options, $default );
1078
1079 if ( '' === $field_options[ $option_name ] ) {
1080 $field_options[ $option_name ] = $default;
1081 }
1082 }
1083
1084 $field_options['grouped'] = 1;
1085
1086 if ( empty( $field_options[ $args->type . '_object' ] ) ) {
1087 $field_options[ $args->type . '_object' ] = '';
1088 }
1089
1090 if ( empty( $field_options[ $args->type . '_val' ] ) ) {
1091 $field_options[ $args->type . '_val' ] = '';
1092 }
1093
1094 // Unset the table info for now.
1095 $field_options['table_info'] = [];
1096
1097 $custom = pods_v( $args->type . '_custom', $field_options, false );
1098
1099 $custom = apply_filters( 'pods_form_ui_field_pick_custom_values', $custom, $args->name, $args->value, $field_options, $args->pod, $args->id );
1100
1101 $ajax = false;
1102
1103 if ( $this->can_ajax( $args->type, $field_options ) ) {
1104 $ajax = true;
1105
1106 $field_data = pods_static_cache_get( $field_options['name'] . '/' . $field_options['id'], __CLASS__ . '/field_data' ) ?: [];
1107
1108 if ( isset( $field_data['autocomplete'] ) ) {
1109 $ajax = (boolean) $field_data['autocomplete'];
1110 }
1111 }
1112
1113 $ajax = apply_filters( 'pods_form_ui_field_pick_ajax', $ajax, $args->name, $args->value, $field_options, $args->pod, $args->id );
1114
1115 if ( 0 === (int) pods_v( $args->type . '_ajax', $field_options, 1 ) ) {
1116 $ajax = false;
1117 }
1118
1119 $field_options[ $args->type . '_ajax' ] = (int) $ajax;
1120
1121 $format_type = pods_v( $args->type . '_format_type', $field_options, 'single', true );
1122
1123 $limit = 1;
1124
1125 if ( 'single' === $format_type ) {
1126 $format_single = pods_v( $args->type . '_format_single', $field_options, 'dropdown', true );
1127
1128 if ( ! empty( $args->value ) && is_array( $args->value ) ) {
1129 $args->value = reset( $args->value );
1130 }
1131
1132 if ( 'dropdown' === $format_single ) {
1133 $field_options['view_name'] = 'select';
1134 } elseif ( 'radio' === $format_single ) {
1135 $field_options['view_name'] = 'radio';
1136 } elseif ( 'autocomplete' === $format_single ) {
1137 $field_options['view_name'] = 'select2';
1138 } elseif ( 'list' === $format_single ) {
1139 $field_options['view_name'] = 'list';
1140 } else {
1141 $field_options['view_name'] = $format_single;
1142 }
1143 } elseif ( 'multi' === $format_type ) {
1144 $format_multi = pods_v( $args->type . '_format_multi', $field_options, 'checkbox', true );
1145
1146 if ( ! empty( $args->value ) && ! is_array( $args->value ) ) {
1147 $args->value = explode( ',', $args->value );
1148 }
1149
1150 if ( 'checkbox' === $format_multi ) {
1151 $field_options['view_name'] = 'checkbox';
1152 } elseif ( 'multiselect' === $format_multi ) {
1153 $field_options['view_name'] = 'select';
1154 } elseif ( 'autocomplete' === $format_multi ) {
1155 $field_options['view_name'] = 'select2';
1156 } elseif ( 'list' === $format_multi ) {
1157 $field_options['view_name'] = 'list';
1158 } else {
1159 $field_options['view_name'] = $format_multi;
1160 }
1161
1162 $limit = 0;
1163
1164 if ( ! empty( $field_options[ $args->type . '_limit' ] ) ) {
1165 $limit = absint( $field_options[ $args->type . '_limit' ] );
1166 }
1167 } else {
1168 $field_options['view_name'] = $format_type;
1169 }
1170
1171 $field_options[ $args->type . '_limit' ] = $limit;
1172
1173 $field_options['ajax_data'] = $this->build_dfv_autocomplete_ajax_data( $field_options, $args, $ajax );
1174 $field_options['select2_overrides'] = null;
1175
1176 if ( 'select2' === $field_options['view_name'] ) {
1177 // @todo Revisit this, they probably aren't used anymore now since this is DFV.
1178 wp_enqueue_style( 'pods-select2' );
1179 wp_enqueue_script( 'pods-select2' );
1180
1181 /**
1182 * Allow overriding some Select2/SelectWoo options used in the JS init.
1183 *
1184 * @since 2.7.0
1185 *
1186 * @param array|null $select2_overrides Override options for Select2/SelectWoo.
1187 */
1188 $field_options['select2_overrides'] = apply_filters( 'pods_pick_select2_overrides', $field_options['select2_overrides'] );
1189 }
1190
1191 if ( ! empty( $args->build_item_data ) ) {
1192 $field_options['item_data'] = $this->build_dfv_field_item_data( $args );
1193 }
1194
1195 return $field_options;
1196 }
1197
1198 /**
1199 * Build DFV autocomplete AJAX data.
1200 *
1201 * @param array|Field $options DFV options.
1202 * @param object $args {
1203 * Field information arguments.
1204 *
1205 * @type string $name Field name.
1206 * @type string $type Field type.
1207 * @type array $options Field options.
1208 * @type mixed $value Current value.
1209 * @type array $pod Pod information.
1210 * @type int|string $id Current item ID.
1211 * }
1212 *
1213 * @param bool $ajax True if ajax mode should be used.
1214 *
1215 * @return array
1216 */
1217 public function build_dfv_autocomplete_ajax_data( $options, $args, $ajax = false ) {
1218
1219 if ( is_array( $args->pod ) ) {
1220 $pod_name = $args->pod['name'];
1221 } elseif ( $args->pod instanceof Pods ) {
1222 $pod_name = $args->pod->pod;
1223 } elseif ( $args->pod instanceof Pod ) {
1224 $pod_name = $args->pod->get_name();
1225 } else {
1226 $pod_name = '';
1227 }
1228
1229 if ( is_array( $options ) ) {
1230 $field_name = pods_v( 'name', $options );
1231 } elseif ( $options instanceof Field ) {
1232 $field_name = $options->get_name();
1233 } else {
1234 $field_name = '';
1235 }
1236
1237 $id = (int) $args->id;
1238
1239 if ( is_user_logged_in() ) {
1240 $uid = 'user_' . get_current_user_id();
1241 } else {
1242 $uid = pods_session_id();
1243 }
1244
1245 $uri_hash = wp_create_nonce( 'pods_uri_' . $_SERVER['REQUEST_URI'] );
1246
1247 $nonce_name = 'pods_relationship:' . json_encode( compact( 'pod_name', 'field_name', 'uid', 'uri_hash', 'id' ) );
1248 $field_nonce = wp_create_nonce( $nonce_name );
1249
1250 // Values can be overridden via the `pods_field_dfv_data` filter in $data['fieldConfig']['ajax_data'].
1251 return [
1252 'ajax' => $ajax,
1253 'delay' => 300,
1254 'minimum_input_length' => 1,
1255 'pod_name' => $pod_name,
1256 'field_name' => $field_name,
1257 'id' => $id,
1258 'uri_hash' => $uri_hash,
1259 '_wpnonce' => $field_nonce,
1260 ];
1261 }
1262
1263 /**
1264 * {@inheritdoc}
1265 */
1266 public function build_dfv_field_config( $args ) {
1267 $config = parent::build_dfv_field_config( $args );
1268
1269 // Ensure data is passed in for relationship fields.
1270 if ( ! isset( $config['data'] ) && ! empty( $args->options['data'] ) ) {
1271 $config['data'] = $this->get_raw_data( $args->options );
1272 }
1273
1274 // Default optgroup handling to off.
1275 if ( ! isset( $config['optgroup'] ) ) {
1276 $config['optgroup'] = false;
1277 }
1278
1279 /**
1280 * Filter on whether to allow modals to be used on the front of the site (in an non-admin area).
1281 *
1282 * @param boolean $show_on_front
1283 * @param array $config
1284 * @param object $args
1285 *
1286 * @since 2.7.0
1287 */
1288 $show_on_front = apply_filters( 'pods_ui_dfv_pick_modals_show_on_front', false, $config, $args );
1289
1290 /**
1291 * Filter on whether to allow nested modals to be used (modals within modals).
1292 *
1293 * @param boolean $allow_nested_modals
1294 * @param array $config
1295 * @param object $args
1296 *
1297 * @since 2.7.0
1298 */
1299 $allow_nested_modals = apply_filters( 'pods_ui_dfv_pick_modals_allow_nested', false, $config, $args );
1300
1301 // Disallow add/edit outside the admin and when we're already in a modal
1302 if ( ( ! $show_on_front && ! is_admin() ) || ( ! $allow_nested_modals && pods_is_modal_window() ) ) {
1303 $config[ $args->type . '_allow_add_new' ] = false;
1304 $config[ $args->type . '_show_edit_link' ] = false;
1305 }
1306
1307 $config[ $args->type . '_taggable' ] = filter_var( pods_v( $args->type . '_taggable', $config ), FILTER_VALIDATE_BOOLEAN );
1308 $config[ $args->type . '_allow_add_new' ] = filter_var( pods_v( $args->type . '_allow_add_new', $config ), FILTER_VALIDATE_BOOLEAN );
1309 $config[ $args->type . '_show_edit_link' ] = filter_var( pods_v( $args->type . '_show_edit_link', $config ), FILTER_VALIDATE_BOOLEAN );
1310
1311 $iframe = [
1312 'src' => '',
1313 'url' => '',
1314 'query_args' => [],
1315 ];
1316
1317 // Set the file name and args based on the content type of the relationship
1318 switch ( $args->options['pick_object'] ) {
1319 case 'post_type':
1320 if ( ! empty( $args->options['pick_val'] ) ) {
1321 $post_type_obj = get_post_type_object( $args->options['pick_val'] );
1322
1323 if ( $post_type_obj && current_user_can( $post_type_obj->cap->create_posts ) ) {
1324 $iframe['url'] = admin_url( 'post-new.php' );
1325 $iframe['query_args'] = [
1326 'post_type' => $args->options['pick_val'],
1327 ];
1328 }
1329 }
1330
1331 // Determine the default icon to use for this post type,
1332 // default to the dashicon for posts
1333 $config['default_icon'] = 'dashicons-admin-post';
1334
1335 // Any custom specified menu icon gets priority
1336 $post_type = get_post_type_object( $args->options['pick_val'] );
1337 if ( ! empty( $post_type->menu_icon ) ) {
1338 $config['default_icon'] = $post_type->menu_icon;
1339
1340 // Page and attachment have their own dashicons
1341 } elseif ( isset( $post_type->name ) ) {
1342 switch ( $post_type->name ) {
1343 case 'page':
1344 // Default for pages.
1345 $config['default_icon'] = 'dashicons-admin-page';
1346 break;
1347 case 'attachment':
1348 // Default for attachments.
1349 $config['default_icon'] = 'dashicons-admin-media';
1350 break;
1351 }
1352 }
1353
1354 break;
1355
1356 case 'taxonomy':
1357 /*
1358 * @todo Fix add new modal issues
1359 if ( ! empty( $args->options['pick_val'] ) ) {
1360 $taxonomy_obj = get_taxonomy( $args->options['pick_val'] );
1361
1362 if ( $taxonomy_obj && current_user_can( $taxonomy_obj->cap->edit_terms ) ) {
1363 $iframe['url'] = admin_url( 'edit-tags.php' );
1364 $iframe['query_args'] = array(
1365 'taxonomy' => $args->options['pick_val'],
1366 );
1367 }
1368 }
1369 */
1370
1371 break;
1372
1373 case 'user':
1374 if ( current_user_can( 'create_users' ) ) {
1375 $iframe['url'] = admin_url( 'user-new.php' );
1376 }
1377
1378 break;
1379
1380 case 'pod':
1381 if ( ! empty( $args->options['pick_val'] ) ) {
1382 if ( pods_is_admin( [ 'pods', 'pods_content', 'pods_edit_' . $args->options['pick_val'] ] ) ) {
1383 $iframe['url'] = admin_url( 'admin.php' );
1384 $iframe['query_args'] = [
1385 'page' => 'pods-manage-' . $args->options['pick_val'],
1386 'action' => 'add',
1387 ];
1388
1389 }
1390 }
1391
1392 break;
1393 }//end switch
1394
1395 // Potential valid modal target if we've set the file name
1396 if ( ! empty( $iframe['url'] ) ) {
1397 // @todo: Replace string literal with defined constant
1398 $iframe['query_args']['pods_modal'] = 1;
1399
1400 // Add args we always need
1401 $iframe['src'] = add_query_arg( $iframe['query_args'], $iframe['url'] );
1402 }
1403
1404 // translators: %s is the field label.
1405 $iframe['title_add'] = sprintf( __( '%s: Add New', 'pods' ), $args->options['label'] );
1406 // translators: %s is the field label.
1407 $iframe['title_edit'] = sprintf( __( '%s: Edit', 'pods' ), $args->options['label'] );
1408
1409 /**
1410 * Allow filtering iframe configuration
1411 *
1412 * @param array $iframe
1413 * @param array $config
1414 * @param object $args
1415 *
1416 * @since 2.7.0
1417 */
1418 $iframe = apply_filters( 'pods_ui_dfv_pick_modals_iframe', $iframe, $config, $args );
1419
1420 if ( ! empty( $iframe['src'] ) ) {
1421 // We extend wp.media.view.Modal for modal add/edit, we must ensure we load the template for it
1422 wp_enqueue_media();
1423 }
1424
1425 $config['iframe_src'] = $iframe['src'];
1426 $config['iframe_title_add'] = $iframe['title_add'];
1427 $config['iframe_title_edit'] = $iframe['title_edit'];
1428
1429 return $config;
1430
1431 }
1432
1433 /**
1434 * {@inheritdoc}
1435 */
1436 public function build_dfv_field_item_data( $args ) {
1437 $args->options['supports_thumbnails'] = null;
1438
1439 $item_data = [];
1440 $data = [];
1441
1442 if ( ! empty( $args->options['data'] ) ) {
1443 $data = $this->get_raw_data( $args->options );
1444 } elseif ( ! empty( $args->data ) ) {
1445 $data = $args->data;
1446 } else {
1447 $data = $this->data( $args->name, $args->value, $args->options, $args->pod, $args->id );
1448 }
1449
1450 if ( [] !== $data ) {
1451 $item_data = $this->build_dfv_field_item_data_recurse( $data, $args );
1452 }
1453
1454 return $item_data;
1455
1456 }
1457
1458 /**
1459 * Loop through relationship data and expand item data with additional information for DFV.
1460 *
1461 * @param array $data Item data to expand.
1462 * @param object $args {
1463 * Field information arguments.
1464 *
1465 * @type string $name Field name.
1466 * @type string $type Field type.
1467 * @type array $options Field options.
1468 * @type mixed $value Current value.
1469 * @type array $pod Pod information.
1470 * @type int|string $id Current item ID.
1471 * }
1472 *
1473 * @return array
1474 */
1475 public function build_dfv_field_item_data_recurse( $data, $args ) {
1476
1477 $item_data = [];
1478
1479 foreach ( $data as $item_id => $item_title ) {
1480 if ( is_array( $item_title ) ) {
1481 $args->options['optgroup'] = true;
1482
1483 $item_data[] = [
1484 'label' => $item_id,
1485 'collection' => $this->build_dfv_field_item_data_recurse( $item_title, $args ),
1486 ];
1487 } else {
1488 // Key by item_id temporarily to be able to sort based on $args->value
1489 $item_data[ $item_id ] = $this->build_dfv_field_item_data_recurse_item( $item_id, $item_title, $args );
1490 }
1491 }
1492
1493 // Maintain any saved sort order from $args->value
1494 if ( is_array( $args->value ) && 1 < count( $args->value ) && $this->is_autocomplete( $args->options ) ) {
1495 $new_item_data = [];
1496
1497 foreach ( $args->value as $value_key => $value_item ) {
1498 if ( ! is_int( $value_key ) ) {
1499 if ( isset( $item_data[ $value_key ] ) ) {
1500 $value_item = $item_data[ $value_key ];
1501 }
1502
1503 $new_item_data[ $value_key ] = $value_item;
1504 }
1505 }
1506
1507 $item_data = array_merge( $new_item_data, $item_data );
1508 }
1509
1510 // Convert from associative to numeric array
1511 return array_values( $item_data );
1512
1513 }
1514
1515 /**
1516 * Loop through relationship data and expand item data with additional information for DFV.
1517 *
1518 * @param int|string $item_id Item ID.
1519 * @param string $item_title Item title.
1520 * @param object $args {
1521 * Field information arguments.
1522 *
1523 * @type string $name Field name.
1524 * @type string $type Field type.
1525 * @type array $options Field options.
1526 * @type mixed $value Current value.
1527 * @type array $pod Pod information.
1528 * @type int|string $id Current item ID.
1529 * }
1530 *
1531 * @return array
1532 */
1533 public function build_dfv_field_item_data_recurse_item( $item_id, $item_title, $args ) {
1534
1535 $icon = '';
1536 $img_icon = '';
1537 $edit_link = '';
1538 $link = '';
1539
1540 if ( ! isset( $args->options['supports_thumbnails'] ) ) {
1541 $args->options['supports_thumbnails'] = null;
1542 }
1543
1544 if ( ! isset( $args->options['pick_object'] ) ) {
1545 $args->options['pick_object'] = null;
1546 }
1547
1548 switch ( $args->options['pick_object'] ) {
1549 case 'post_type':
1550 if ( null === $args->options['supports_thumbnails'] && ! empty( $args->options['pick_val'] ) ) {
1551 $args->options['supports_thumbnails'] = post_type_supports( $args->options['pick_val'], 'thumbnail' );
1552 }
1553
1554 if ( true === $args->options['supports_thumbnails'] ) {
1555 $post_thumbnail_id = get_post_thumbnail_id( $item_id );
1556
1557 if ( $post_thumbnail_id ) {
1558 $thumb = wp_get_attachment_image_src( $post_thumbnail_id, 'thumbnail', false );
1559 }
1560
1561 if ( ! empty( $thumb[0] ) ) {
1562 $img_icon = $thumb[0];
1563 }
1564 }
1565
1566 if ( empty( $img_icon ) ) {
1567
1568 // Default icon for posts.
1569 $icon = 'dashicons-admin-post';
1570
1571 // Post type icons.
1572 $post_type = (array) get_post_type_object( get_post_type( $item_id ) );
1573
1574 if ( ! empty( $post_type['menu_icon'] ) ) {
1575 // Post specific icon.
1576 $icon = $post_type['menu_icon'];
1577 } elseif ( isset( $post_type['name'] ) && 'page' ) {
1578 switch ( $post_type['name'] ) {
1579 case 'page':
1580 // Default for pages.
1581 $icon = 'dashicons-admin-page';
1582 break;
1583 case 'attachment':
1584 // Default for attachments.
1585 $icon = 'dashicons-admin-media';
1586 break;
1587 }
1588 }
1589 }//end if
1590
1591 $edit_link = get_edit_post_link( $item_id, 'raw' );
1592
1593 $link = get_permalink( $item_id );
1594
1595 break;
1596
1597 case 'taxonomy':
1598 if ( ! empty( $args->options['pick_val'] ) ) {
1599
1600 // Default icon for taxonomy.
1601 $icon = 'dashicons-category';
1602
1603 // Change icon for non-hierarchical taxonomies.
1604 $taxonomy = get_term( $item_id );
1605 if ( isset( $taxonomy->taxonomy ) ) {
1606 $taxonomy = (array) get_taxonomy( $taxonomy->taxonomy );
1607 if ( isset( $taxonomy['hierarchical'] ) && ! $taxonomy['hierarchical'] ) {
1608 $icon = 'dashicons-tag';
1609 }
1610 }
1611
1612 $edit_link = get_edit_term_link( $item_id, $args->options['pick_val'] );
1613
1614 $link = get_term_link( $item_id, $args->options['pick_val'] );
1615 }
1616
1617 break;
1618
1619 case 'user':
1620 $args->options['supports_thumbnails'] = true;
1621
1622 $icon = 'dashicons-admin-users';
1623 $img_icon = get_avatar_url( $item_id, [ 'size' => 150 ] );
1624
1625 $edit_link = get_edit_user_link( $item_id );
1626
1627 $link = get_author_posts_url( $item_id );
1628
1629 break;
1630
1631 case 'comment':
1632 $args->options['supports_thumbnails'] = true;
1633
1634 $icon = 'dashicons-admin-comments';
1635 $img_icon = get_avatar_url( get_comment( $item_id ), [ 'size' => 150 ] );
1636
1637 $edit_link = get_edit_comment_link( $item_id );
1638
1639 $link = get_comment_link( $item_id );
1640
1641 break;
1642
1643 case 'pod':
1644 if ( ! empty( $args->options['pick_val'] ) ) {
1645
1646 $icon = pods_svg_icon( 'pods' );
1647
1648 if ( pods_is_admin( [ 'pods', 'pods_content', 'pods_edit_' . $args->options['pick_val'] ] ) ) {
1649 $file_name = 'admin.php';
1650 $query_args = [
1651 'page' => 'pods-manage-' . $args->options['pick_val'],
1652 'action' => 'edit',
1653 'id' => $item_id,
1654 ];
1655
1656 $edit_link = add_query_arg( $query_args, admin_url( $file_name ) );
1657 }
1658
1659 // @todo Add $link support
1660 $link = '';
1661 }
1662
1663 break;
1664 }//end switch
1665
1666 // Image icons always overwrite default icons
1667 if ( ! empty( $img_icon ) ) {
1668 $icon = $img_icon;
1669 }
1670
1671 // Parse icon type
1672 if ( 'none' === $icon || 'div' === $icon ) {
1673 $icon = '';
1674 } elseif ( 0 === strpos( $icon, 'dashicons-' ) ) {
1675 $icon = sanitize_html_class( $icon );
1676 }
1677
1678 // #5740 Check for WP_Error object.
1679 if ( ! is_string( $link ) ) {
1680 $link = '';
1681 }
1682
1683 // Support modal editing
1684 if ( ! empty( $edit_link ) ) {
1685 // @todo: Replace string literal with defined constant
1686 $edit_link = add_query_arg( [ 'pods_modal' => '1' ], $edit_link );
1687 }
1688
1689 // Determine if this is a selected item.
1690 // Issue history for setting selected: #4753, #4892, #5014.
1691 $selected = false;
1692
1693 $values = [];
1694
1695 // If we have values, let's cast them.
1696 if ( isset( $args->value ) ) {
1697 // The value may be a single non-array value.
1698 $values = (array) $args->value;
1699 }
1700
1701 // Cast values in array as string.
1702 $values = array_map( static function ( $value ) {
1703 if ( ! is_scalar( $value ) ) {
1704 return $value;
1705 }
1706
1707 return (string) $value;
1708 }, $values );
1709
1710 // If the value array has keys as IDs, let's check for matches from the keys first.
1711 if ( ! isset( $values[0] ) ) {
1712 // Get values from keys.
1713 $key_values = array_keys( $values );
1714
1715 // Cast key values in array as string.
1716 $key_values = array_map( static function ( $value ) {
1717 if ( ! is_scalar( $value ) ) {
1718 return $value;
1719 }
1720
1721 return (string) $value;
1722 }, $key_values );
1723
1724 // Let's check to see if the current $item_id matches any key values.
1725 if ( in_array( (string) $item_id, $key_values, true ) ) {
1726 $selected = true;
1727 }
1728 }
1729
1730 // If we do not have a key match, the normal values may still match.
1731 if ( ! $selected ) {
1732 // Let's check to see if the current $item_id matches any values.
1733 if ( in_array( (string) $item_id, $values, true ) ) {
1734 $selected = true;
1735 }
1736 }
1737
1738 $item = [
1739 'id' => null !== $item_id ? html_entity_decode( esc_html( $item_id ), ENT_COMPAT ) : '',
1740 'icon' => null !== $icon ? esc_attr( $icon ) : '',
1741 'name' => null !== $item_title ? wp_strip_all_tags( html_entity_decode( $item_title, ENT_COMPAT ) ) : '',
1742 'edit_link' => null !== $edit_link ? html_entity_decode( esc_url( $edit_link ), ENT_COMPAT ) : '',
1743 'link' => null !== $link ? html_entity_decode( esc_url( $link ), ENT_COMPAT ) : '',
1744 'selected' => $selected,
1745 ];
1746
1747 return $item;
1748
1749 }
1750
1751 /**
1752 * {@inheritdoc}
1753 */
1754 public function validate( $value, $name = null, $options = null, $fields = null, $pod = null, $id = null, $params = null ) {
1755 $validate = parent::validate( $value, $name, $options, $fields, $pod, $id, $params );
1756
1757 $errors = [];
1758
1759 if ( is_array( $validate ) ) {
1760 $errors = $validate;
1761 }
1762
1763 if ( empty( self::$api ) ) {
1764 self::$api = pods_api();
1765 }
1766
1767 $field = pods_config_for_field( $options, $pod );
1768
1769 $related_pick_limit = 0;
1770 $related_field = false;
1771 $related_pod = false;
1772 $current_related_ids = false;
1773
1774 // Bidirectional relationship requirement checks
1775 $related_object = pods_v( static::$type . '_object', $options, '' );
1776 // pod, post_type, taxonomy, etc..
1777 $related_val = pods_v( static::$type . '_val', $options, $related_object, null, true );
1778 // pod name, post type name, taxonomy name, etc..
1779 if ( empty( $related_val ) ) {
1780 $related_val = $related_object;
1781 }
1782
1783 $related_sister_id = pods_v( 'sister_id', $options, 0 );
1784
1785 if ( is_numeric( $related_sister_id ) ) {
1786 $related_sister_id = (int) $related_sister_id;
1787 } else {
1788 $related_sister_id = 0;
1789 }
1790
1791 $options['id'] = (int) $options['id'];
1792
1793 $related_data = pods_static_cache_get( $options['name'] . '/' . $options['id'], __CLASS__ . '/related_data' ) ?: [];
1794
1795 if (
1796 ! empty( $related_sister_id )
1797 && ! empty( $related_object )
1798 && $field
1799 && ! $field->is_simple_relationship()
1800 ) {
1801 $related_pod = self::$api->load_pod( [
1802 'name' => $related_val,
1803 'auto_setup' => true,
1804 ] );
1805
1806 if ( false !== $related_pod && ( 'pod' === $related_object || $related_object === $related_pod['type'] ) ) {
1807 $related_field = false;
1808
1809 // Ensure sister_id exists on related Pod.
1810 foreach ( $related_pod['fields'] as $related_pod_field ) {
1811 if ( 'pick' === $related_pod_field['type'] && $related_sister_id === $related_pod_field['id'] ) {
1812 $related_field = $related_pod_field;
1813
1814 break;
1815 }
1816 }
1817
1818 if ( ! empty( $related_field ) ) {
1819 $current_ids = self::$api->lookup_related_items( $options['id'], $pod['id'], $id, $options, $pod );
1820
1821 $related_data[ 'current_ids_' . $id ] = $current_ids;
1822
1823 $value_ids = $value;
1824
1825 // Convert values from a comma-separated string into an array.
1826 if ( ! is_array( $value_ids ) ) {
1827 $value_ids = explode( ',', $value_ids );
1828 }
1829
1830 $value_ids = array_unique( array_filter( $value_ids ) );
1831
1832 // Get ids to remove.
1833 $remove_ids = array_diff( $current_ids, $value_ids );
1834
1835 $related_data[ 'remove_ids_' . $id ] = $remove_ids;
1836
1837 $related_required = (boolean) pods_v( 'required', $related_field, 0 );
1838 $related_pick_limit = (int) pods_v( static::$type . '_limit', $related_field, 0 );
1839
1840 if ( 'single' === pods_v( static::$type . '_format_type', $related_field ) ) {
1841 $related_pick_limit = 1;
1842 }
1843
1844 // Validate Required fields.
1845 if ( $related_required && ! empty( $remove_ids ) ) {
1846 foreach ( $remove_ids as $related_id ) {
1847 $bidirectional_ids = self::$api->lookup_related_items( $related_field['id'], $related_pod['id'], $related_id, $related_field, $related_pod );
1848
1849 $related_data[ 'related_ids_' . $related_id ] = $bidirectional_ids;
1850
1851 if ( empty( $bidirectional_ids ) || ( in_array( (int) $id, $bidirectional_ids, true ) && 1 === count( $bidirectional_ids ) ) ) {
1852 // Translators: %1$s and %2$s stand for field labels.
1853 $errors[] = sprintf( esc_html__( 'The %1$s field is required and cannot be removed by the %2$s field', 'pods' ), $related_field['label'], $options['label'] );
1854
1855 return $errors;
1856 }
1857 }
1858 }
1859 } else {
1860 $related_pod = false;
1861 }//end if
1862 } else {
1863 $related_pod = false;
1864 }//end if
1865 }//end if
1866
1867 if ( ! empty( $related_data ) ) {
1868 $related_data['related_pod'] = $related_pod;
1869 $related_data['related_field'] = $related_field;
1870 $related_data['related_pick_limit'] = $related_pick_limit;
1871
1872 pods_static_cache_set( $options['name'] . '/' . $options['id'], $related_data, __CLASS__ . '/related_data' );
1873
1874 $pick_limit = (int) pods_v( static::$type . '_limit', $options, 0 );
1875
1876 if ( 'single' === pods_v( static::$type . '_format_type', $options ) ) {
1877 $pick_limit = 1;
1878 }
1879
1880 $related_field['id'] = (int) $related_field['id'];
1881
1882 $bidirectional_related_data = pods_static_cache_get( $related_field['name'] . '/' . $related_field['id'], __CLASS__ . '/related_data' ) ?: [];
1883
1884 if ( empty( $bidirectional_related_data ) ) {
1885 $bidirectional_related_data = [
1886 'related_pod' => $pod,
1887 'related_field' => $options,
1888 'related_pick_limit' => $pick_limit,
1889 ];
1890
1891 pods_static_cache_set( $related_field['name'] . '/' . $related_field['id'], $bidirectional_related_data, __CLASS__ . '/related_data' );
1892 }
1893 }//end if
1894
1895 if ( ! empty( $errors ) ) {
1896 return $errors;
1897 }
1898
1899 return $validate;
1900
1901 }
1902
1903 /**
1904 * {@inheritdoc}
1905 */
1906 public function save( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) {
1907
1908 if ( empty( self::$api ) ) {
1909 self::$api = pods_api();
1910 }
1911
1912 $options['id'] = (int) $options['id'];
1913
1914 $related_pod = null;
1915 $related_field = null;
1916 $related_pick_limit = 0;
1917 $current_ids = [];
1918 $remove_ids = [];
1919
1920 if ( null === $value ) {
1921 $value = [];
1922 } elseif ( ! is_array( $value ) ) {
1923 $value = [
1924 $value,
1925 ];
1926 }
1927
1928 $current_ids = isset( $params->current_ids ) ? $params->current_ids : null;
1929 $value_ids = isset( $params->value_ids ) ? $params->value_ids : null;
1930 $remove_ids = isset( $params->remove_ids ) ? $params->remove_ids : null;
1931
1932 if ( null === $value_ids ) {
1933 $value_ids = array_unique( array_filter( $value ) );
1934 }
1935
1936 $related_data = pods_static_cache_get( $options['name'] . '/' . $options['id'], __CLASS__ . '/related_data' ) ?: [];
1937
1938 if ( ! empty( $related_data ) && isset( $related_data[ 'current_ids_' . $id ], $related_data[ 'remove_ids_' . $id ] ) ) {
1939 $related_pod = $related_data['related_pod'];
1940 $related_field = $related_data['related_field'];
1941 $related_pick_limit = $related_data['related_pick_limit'];
1942
1943 if ( null === $current_ids ) {
1944 $current_ids = $related_data[ 'current_ids_' . $id ];
1945 }
1946
1947 if ( null === $remove_ids ) {
1948 $remove_ids = $related_data[ 'remove_ids_' . $id ];
1949 }
1950 } elseif ( $options instanceof Field || $options instanceof Value_Field ) {
1951 $related_field = $options->get_bidirectional_field();
1952
1953 if ( $related_field ) {
1954 $related_pod = $related_field->get_parent_object();
1955 $related_pick_limit = $related_field->get_limit();
1956
1957 if ( null === $current_ids ) {
1958 $current_ids = self::$api->lookup_related_items( $options['id'], $pod['id'], $id, $options, $pod );
1959 }
1960
1961 // Get ids to remove.
1962 if ( null === $remove_ids ) {
1963 $remove_ids = array_diff( $current_ids, $value_ids );
1964 }
1965 }
1966 }
1967
1968 // Handle syncing of taxonomy terms first (doesn't require bidirectional relationship).
1969 if ( ! empty( $pod['type'] ) && 'post_type' === $pod['type'] && pods_v_bool( static::$type . '_sync_taxonomy', $options ) ) {
1970 // Check if post type has the same attached taxonomy.
1971 $taxonomies_available = get_object_taxonomies( $pod['name'] );
1972 $taxonomies_available = array_flip( $taxonomies_available );
1973
1974 if ( $options instanceof Field || $options instanceof Value_Field ) {
1975 $taxonomy_name = $options->get_related_object_name();
1976
1977 if ( isset( $taxonomies_available[ $taxonomy_name ] ) ) {
1978 /**
1979 * Allow filtering the list of term IDs that will be synced for a field.
1980 *
1981 * @since 3.2.4
1982 *
1983 * @param array $term_ids_to_sync The list of term IDs to sync.
1984 * @param Field $field The field object.
1985 * @param int $id The post ID.
1986 * @param string $taxonomy_name The taxonomy name being synced.
1987 */
1988 $term_ids_to_sync = apply_filters( 'pods_field_pick_save_sync_taxonomy_term_ids', $value_ids, $options, $id, $taxonomy_name );
1989
1990 // Update the taxonomy terms for the current post.
1991 wp_set_post_terms( $id, $term_ids_to_sync, $taxonomy_name );
1992 }
1993 }
1994 }
1995
1996 // Exit early if no bidirectional relationship to handle.
1997 if ( empty( $related_field ) || empty( $related_pod ) ) {
1998 return;
1999 }
2000
2001 // Handle the bi-directional relationship updates.
2002
2003 $no_conflict = true;
2004
2005 // Only check no conflict mode if this isn't the current pod type.
2006 if ( $related_pod['type'] !== $pod['type'] ) {
2007 $no_conflict = pods_no_conflict_check( $related_pod['type'] );
2008 }
2009
2010 if ( ! $no_conflict ) {
2011 pods_no_conflict_on( $related_pod['type'] );
2012 }
2013
2014 if ( empty( $value_ids ) ) {
2015 // Remove all bidirectional relationships.
2016 if ( ! empty( $remove_ids ) ) {
2017 // Remove this ID from the related IDS.
2018 self::$api->delete_relationships( $remove_ids, $id, $related_pod, $related_field );
2019
2020 // Remove the related IDs from this ID.
2021 self::$api->delete_relationships( $id, $remove_ids, $pod, $options );
2022 }
2023
2024 if ( ! $no_conflict ) {
2025 pods_no_conflict_off( $related_pod['type'] );
2026 }
2027
2028 return;
2029 }
2030
2031 foreach ( $value_ids as $related_id ) {
2032 if ( ! empty( $related_data[ 'related_ids_' . $related_id ] ) ) {
2033 $bidirectional_ids = $related_data[ 'related_ids_' . $related_id ];
2034 } else {
2035 $bidirectional_ids = self::$api->lookup_related_items( $related_field['id'], $related_pod['id'], $related_id, $related_field, $related_pod );
2036 }
2037
2038 $bidirectional_ids = array_filter( $bidirectional_ids );
2039
2040 if ( empty( $bidirectional_ids ) ) {
2041 $bidirectional_ids = [];
2042 }
2043
2044 $bidirectional_remove_ids = [];
2045
2046 if ( 0 < $related_pick_limit && ! empty( $bidirectional_ids ) && ! in_array( $id, $bidirectional_ids, true ) ) {
2047 $total_bidirectional_ids = count( $bidirectional_ids );
2048
2049 while ( $related_pick_limit <= $total_bidirectional_ids ) {
2050 $bidirectional_remove_ids[] = (int) array_pop( $bidirectional_ids );
2051
2052 $total_bidirectional_ids = count( $bidirectional_ids );
2053 }
2054 }
2055
2056 // Remove this item from related items no longer related to.
2057 $bidirectional_remove_ids = array_unique( array_filter( $bidirectional_remove_ids ) );
2058
2059 if ( ! in_array( $id, $bidirectional_ids, true ) ) {
2060 // Add to related items.
2061 $bidirectional_ids[] = $id;
2062 } elseif ( empty( $bidirectional_remove_ids ) ) {
2063 // Nothing to change.
2064 continue;
2065 }
2066
2067 self::$api->save_relationships( $related_id, $bidirectional_ids, $related_pod, $related_field );
2068
2069 if ( ! empty( $bidirectional_remove_ids ) ) {
2070 self::$api->delete_relationships( $bidirectional_remove_ids, $related_id, $pod, $options );
2071 }
2072 }//end foreach
2073
2074 // Remove this ID from the related IDs.
2075 if ( ! empty( $remove_ids ) ) {
2076 self::$api->delete_relationships( $remove_ids, $id, $related_pod, $related_field );
2077 }
2078
2079 if ( ! $no_conflict ) {
2080 pods_no_conflict_off( $related_pod['type'] );
2081 }
2082 }
2083
2084 /**
2085 * Delete the value from the DB
2086 *
2087 * @param int|null $id Item ID.
2088 * @param string|null $name Field name.
2089 * @param array|null $options Field options.
2090 * @param array|null $pod Pod options.
2091 *
2092 * @since 2.3.0
2093 */
2094 public function delete( $id = null, $name = null, $options = null, $pod = null ) {
2095
2096 if ( empty( self::$api ) ) {
2097 self::$api = pods_api();
2098 }
2099
2100 $field = pods_config_for_field( $options, $pod );
2101
2102 // Bidirectional relationship requirement checks.
2103 $related_object = pods_v( static::$type . '_object', $options, '' );
2104
2105 // pod, post_type, taxonomy, etc..
2106 $related_val = pods_v( static::$type . '_val', $options, $related_object, true );
2107
2108 // pod name, post type name, taxonomy name, etc..
2109 $related_sister_id = pods_v( 'sister_id', $options, 0 );
2110
2111 if ( is_numeric( $related_sister_id ) ) {
2112 $related_sister_id = (int) $related_sister_id;
2113 } else {
2114 $related_sister_id = 0;
2115 }
2116
2117 if (
2118 ! empty( $related_sister_id )
2119 && ! empty( $related_object )
2120 && $field
2121 && ! $field->is_simple_relationship()
2122 ) {
2123 $related_pod = self::$api->load_pod( [
2124 'name' => $related_val,
2125 'auto_setup' => true,
2126 ] );
2127
2128 if ( false !== $related_pod && ( 'pod' === $related_object || $related_object === $related_pod['type'] ) ) {
2129 $related_field = false;
2130
2131 // Ensure sister_id exists on related Pod.
2132 foreach ( $related_pod['fields'] as $related_pod_field ) {
2133 if ( 'pick' === $related_pod_field['type'] && (int) $related_sister_id === (int) $related_pod_field['id'] ) {
2134 $related_field = $related_pod_field;
2135
2136 break;
2137 }
2138 }
2139
2140 if ( ! empty( $related_field ) ) {
2141 $values = self::$api->lookup_related_items( $options['id'], $pod['id'], $id, $options, $pod );
2142
2143 if ( ! empty( $values ) ) {
2144 $no_conflict = pods_no_conflict_check( $related_pod['type'] );
2145
2146 if ( ! $no_conflict ) {
2147 pods_no_conflict_on( $related_pod['type'] );
2148 }
2149
2150 self::$api->delete_relationships( $values, $id, $related_pod, $related_field );
2151
2152 if ( ! $no_conflict ) {
2153 pods_no_conflict_off( $related_pod['type'] );
2154 }
2155 }
2156 }
2157 }//end if
2158 }//end if
2159
2160 }
2161
2162 /**
2163 * {@inheritdoc}
2164 */
2165 public function ui( $id, $value, $name = null, $options = null, $fields = null, $pod = null ) {
2166
2167 $value = $this->simple_value( $name, $value, $options, $pod, $id );
2168
2169 return $this->display( $value, $name, $options, $pod, $id );
2170
2171 }
2172
2173 /**
2174 * Get the raw data from the field data provided.
2175 *
2176 * @since 2.9.9
2177 *
2178 * @param array|Field $field The field data.
2179 *
2180 * @return array|mixed
2181 */
2182 public function get_raw_data( $field ) {
2183 $data = pods_v( 'data', $field, null, true );
2184
2185 if ( null !== $data ) {
2186 // Support late-initializing the data from a callback function passed in.
2187 if ( ! is_array( $data ) && ! is_string( $data ) && is_callable( $data ) ) {
2188 $data = $data();
2189 }
2190
2191 $data = (array) $data;
2192 }
2193
2194 return $data;
2195 }
2196
2197 /**
2198 * {@inheritdoc}
2199 */
2200 public function data( $name, $value = null, $options = null, $pod = null, $id = null, $in_form = true ) {
2201 $data = $this->get_raw_data( $options );
2202
2203 $object_params = [
2204 // The name of the field.
2205 'name' => $name,
2206 // The value of the field.
2207 'value' => $value,
2208 // Field options.
2209 'options' => $options,
2210 // Pod data.
2211 'pod' => $pod,
2212 // Item ID.
2213 'id' => $id,
2214 // Data context.
2215 'context' => 'data',
2216 ];
2217
2218 if ( null !== $data ) {
2219 $data = (array) $data;
2220 } else {
2221 $data = (array) $this->get_object_data( $object_params );
2222 }
2223
2224 /**
2225 * Allow filtering whether to always show default select text for the Single select Dropdown field even if
2226 * the field is required.
2227 *
2228 * @since 2.8.9
2229 *
2230 * @param bool $always_show_default_select_text Whether to always show default select text.
2231 * @param array $data The object data.
2232 * @param string|null $name Field name.
2233 * @param mixed|null $value Current value.
2234 * @param array|null $options Field options.
2235 * @param array|null $pod Pod information.
2236 * @param int|string|null $id Current item ID.
2237 */
2238 $always_show_default_select_text = (bool) apply_filters( 'pods_field_pick_always_show_default_select_text', (bool) pods_v( static::$type . '_select_text_always_show', $options, false ), $data, $name, $value, $options, $pod, $id );
2239
2240 if (
2241 'single' === pods_v( static::$type . '_format_type', $options, 'single' )
2242 && 'dropdown' === pods_v( static::$type . '_format_single', $options, 'dropdown' )
2243 //&& 0 !== (int) pods_v( static::$type . '_show_select_text', $options, 1 )
2244 && (
2245 $always_show_default_select_text
2246 || empty( $value )
2247 || 0 === (int) pods_v( 'required', $options, 0 )
2248 )
2249 ) {
2250 $default_select = [
2251 '' => pods_v( static::$type . '_select_text', $options, __( '-- Select One --', 'pods' ), true ),
2252 ];
2253
2254 // Unset to prevent conflict.
2255 if ( isset( $data[''] ) ) {
2256 unset( $data[''] );
2257 }
2258
2259 // Prevent resetting the numeric ID keys when adding the empty option via array_merge, use union instead.
2260 $data = $default_select + $data;
2261 }
2262
2263 $data = apply_filters( 'pods_field_pick_data', $data, $name, $value, $options, $pod, $id );
2264
2265 return $data;
2266
2267 }
2268
2269 /**
2270 * Convert a simple value to the correct value
2271 *
2272 * @param string $name The name of the field.
2273 * @param string|array|null $value The value of the field.
2274 * @param array|null $options Field options.
2275 * @param array|null $pod Pod data.
2276 * @param int|null $id Item ID.
2277 * @param boolean $raw Whether to return the raw list of keys (true) or convert to key=>value (false).
2278 *
2279 * @return mixed Corrected value
2280 */
2281 public function simple_value( $name, $value = null, $options = null, $pod = null, $id = null, $raw = false ) {
2282
2283 if ( in_array( pods_v( static::$type . '_object', $options ), $this->simple_objects(), true ) ) {
2284 if ( ! is_array( $value ) && 0 < strlen( (string) $value ) ) {
2285 $simple = @json_decode( $value, true );
2286
2287 if ( is_array( $simple ) ) {
2288 $value = (array) $simple;
2289 }
2290 }
2291
2292 $data = $this->get_raw_data( $options );
2293
2294 $object_params = [
2295 // The name of the field.
2296 'name' => $name,
2297 // The value of the field.
2298 'value' => $value,
2299 // Field options.
2300 'options' => $options,
2301 // Pod data.
2302 'pod' => $pod,
2303 // Item ID.
2304 'id' => $id,
2305 // Data context.
2306 'context' => 'simple_value',
2307 ];
2308
2309 if ( null !== $data ) {
2310 $data = (array) $data;
2311 } else {
2312 $data = (array) $this->get_object_data( $object_params );
2313 }
2314
2315 $key = 0;
2316
2317 if ( is_array( $value ) ) {
2318 if ( ! empty( $data ) ) {
2319 $val = [];
2320
2321 foreach ( $value as $k => $v ) {
2322 if ( is_scalar( $v ) && isset( $data[ $v ] ) ) {
2323 if ( false === $raw ) {
2324 $k = $v;
2325 $v = $data[ $v ];
2326 }
2327
2328 $val[ $k ] = $v;
2329 }
2330 }
2331
2332 $value = $val;
2333 }
2334 } elseif ( isset( $data[ $value ] ) && false === $raw ) {
2335 $key = $value;
2336 $value = $data[ $value ];
2337 }//end if
2338
2339 $single_multi = pods_v( static::$type . '_format_type', $options, 'single' );
2340
2341 if ( 'multi' === $single_multi ) {
2342 $limit = (int) pods_v( static::$type . '_limit', $options, 0 );
2343 } else {
2344 $limit = 1;
2345 }
2346
2347 if ( is_array( $value ) && 0 < $limit ) {
2348 if ( 1 === $limit ) {
2349 $value = current( $value );
2350 } else {
2351 $value = array_slice( $value, 0, $limit, true );
2352 }
2353 } elseif ( ! is_array( $value ) && null !== $value && 0 < strlen( (string) $value ) ) {
2354 if ( 1 !== $limit || ( true === $raw && 'multi' === $single_multi ) ) {
2355 $value = [
2356 $key => $value,
2357 ];
2358 }
2359 }
2360 }//end if
2361
2362 return $value;
2363
2364 }
2365
2366 /**
2367 * Get the label from a pick value.
2368 *
2369 * @param string $name The name of the field.
2370 * @param string|array|null $value The value of the field.
2371 * @param array|null $options Field options.
2372 * @param array|null $pod Pod data.
2373 * @param int|null $id Item ID.
2374 *
2375 * @return string
2376 *
2377 * @since 2.2.0
2378 */
2379 public function value_to_label( $name, $value = null, $options = null, $pod = null, $id = null ) {
2380 $data = $this->get_raw_data( $options );
2381
2382 $object_params = [
2383 // The name of the field.
2384 'name' => $name,
2385 // The value of the field.
2386 'value' => $value,
2387 // Field options.
2388 'options' => $options,
2389 // Pod data.
2390 'pod' => $pod,
2391 // Item ID.
2392 'id' => $id,
2393 // Data context.
2394 'context' => 'value_to_label',
2395 ];
2396
2397 if ( null !== $data ) {
2398 $data = (array) $data;
2399 } else {
2400 $data = (array) $this->get_object_data( $object_params );
2401 }
2402
2403 $labels = [];
2404
2405 foreach ( $data as $v => $l ) {
2406 if ( ! in_array( (string) $l, $labels, true ) && ( (string) $value === (string) $v || ( is_array( $value ) && in_array( (string) $v, $value, true ) ) ) ) {
2407 $labels[] = (string) $l;
2408 }
2409 }
2410
2411 $labels = apply_filters( 'pods_field_pick_value_to_label', $labels, $name, $value, $options, $pod, $id );
2412
2413 $labels = pods_serial_comma( $labels );
2414
2415 return $labels;
2416
2417 }
2418
2419 /**
2420 * Get available items from a relationship field.
2421 *
2422 * @param array|string $field Field array or field name.
2423 * @param array $deprecated Field options array overrides.
2424 * @param array $object_params Additional get_object_data options.
2425 *
2426 * @return array An array of available items from a relationship field
2427 */
2428 public function get_field_data( $field, $deprecated = null, $object_params = [] ) {
2429 $options = [];
2430
2431 $is_field_object = $field instanceof Field;
2432
2433 if ( is_array( $field ) || $is_field_object ) {
2434 $options = $field;
2435 }
2436
2437 // Get field name from array.
2438 $field = pods_v( 'name', $options, $field, true );
2439
2440 // Field name or options not set.
2441 if ( empty( $field ) || empty( $options ) ) {
2442 return [];
2443 }
2444
2445 // Setup object params.
2446 $object_params = array_merge(
2447 [
2448 // The name of the field.
2449 'name' => $field,
2450 // Field options.
2451 'options' => $options,
2452 ], $object_params
2453 );
2454
2455 // Get data override.
2456 $data = $this->get_raw_data( $options );
2457
2458 if ( null !== $data ) {
2459 // Return data override.
2460 $data = (array) $data;
2461 } else {
2462 // Get object data.
2463 $data = $this->get_object_data( $object_params );
2464 }
2465
2466 return $data;
2467
2468 }
2469
2470 /**
2471 * Get data from relationship objects.
2472 *
2473 * @param array $object_params Object data parameters.
2474 *
2475 * @return array|bool Object data
2476 */
2477 public function get_object_data( $object_params = null ) {
2478
2479 /**
2480 * @var $wpdb wpdb
2481 */
2482 global $wpdb;
2483
2484 $object_params = array_merge(
2485 [
2486 // The name of the field.
2487 'name' => '',
2488 // The value of the field.
2489 'value' => '',
2490 // Field options.
2491 'options' => [],
2492 // Pod data.
2493 'pod' => '',
2494 // Item ID.
2495 'id' => '',
2496 // Data context.
2497 'context' => '',
2498 // Data parameters.
2499 'data_params' => [
2500 // Query being searched.
2501 'query' => '',
2502 ],
2503 // Page number of results to get.
2504 'page' => 1,
2505 // How many data items to limit to (autocomplete defaults to 30, set to -1 or 1+ to override).
2506 'limit' => 0,
2507 ], $object_params
2508 );
2509
2510 /**
2511 * Overwrite parameters used by PodsField_Pick::get_object_data.
2512 *
2513 * @since 2.7.21
2514 *
2515 * @param array $object_params {
2516 * Get object parameters
2517 *
2518 * @type string $name Field name.
2519 * @type mixed $value Current value.
2520 * @type array $options Field options.
2521 * @type array $pod Pod data.
2522 * @type int|string $id Current item ID.
2523 * @type string $context Data context.
2524 * @type array $data_params Data parameters.
2525 * @type int $page Page number of results to get.
2526 * @type int $limit How many data items to limit to (autocomplete defaults to 30, set to -1 or 1+ to override).
2527 * }
2528 */
2529 $object_params = apply_filters( 'pods_field_pick_object_data_params', $object_params );
2530
2531 $object_params['data_params'] = (array) $object_params['data_params'];
2532
2533 $name = $object_params['name'];
2534 $value = $object_params['value'];
2535 $options = $object_params['options'];
2536 $pod = $object_params['pod'];
2537 $id = $object_params['id'];
2538 $context = $object_params['context'];
2539 $data_params = $object_params['data_params'];
2540 $page = min( 1, (int) $object_params['page'] );
2541 $limit = (int) $object_params['limit'];
2542 $autocomplete = false;
2543
2544 // Use field object if it was provided.
2545 if ( isset( $options['_field_object'] ) ) {
2546 $options = $options['_field_object'];
2547
2548 $name = $options->get_name();
2549 }
2550
2551 $data = apply_filters( 'pods_field_pick_object_data', null, $name, $value, $options, $pod, $id, $object_params );
2552 $items = [];
2553
2554 if ( ! isset( $options[ static::$type . '_object' ] ) ) {
2555 $data = $this->get_raw_data( $options );
2556 }
2557
2558 $simple = false;
2559
2560 if ( null === $data ) {
2561 $data = [];
2562
2563 $pick_object = pods_v( static::$type . '_object', $options, null, true );
2564
2565 // No pick object means this has no configuration to work from.
2566 if ( empty( $pick_object ) ) {
2567 return $data;
2568 }
2569
2570 if ( 'custom-simple' === $pick_object ) {
2571 $custom = pods_v( static::$type . '_custom', $options, '' );
2572
2573 $custom = apply_filters( 'pods_form_ui_field_pick_custom_values', $custom, $name, $value, $options, $pod, $id, $object_params );
2574
2575 if ( ! empty( $custom ) ) {
2576 if ( ! is_array( $custom ) ) {
2577 $data = [];
2578
2579 $custom = explode( "\n", trim( $custom ) );
2580
2581 foreach ( $custom as $custom_value ) {
2582 $custom_value = trim( trim( $custom_value, '|' ) );
2583 $custom_label = explode( '|', $custom_value );
2584
2585 if ( empty( $custom_label ) ) {
2586 continue;
2587 }
2588
2589 if ( 1 === count( $custom_label ) ) {
2590 $custom_label = $custom_value;
2591 } else {
2592 $custom_value = $custom_label[0];
2593 $custom_label = $custom_label[1];
2594 }
2595
2596 $custom_value = trim( (string) $custom_value );
2597 $custom_label = trim( (string) $custom_label );
2598
2599 $data[ $custom_value ] = $custom_label;
2600 }
2601 } else {
2602 $data = $custom;
2603 }//end if
2604
2605 $simple = true;
2606 }//end if
2607 } elseif (
2608 $pick_object
2609 && $this->setup_related_objects()
2610 && isset( self::$related_objects[ $pick_object ] )
2611 && ! empty( self::$related_objects[ $pick_object ]['data'] )
2612 ) {
2613 $data = self::$related_objects[ $options[ static::$type . '_object' ] ]['data'];
2614
2615 $simple = true;
2616 } elseif (
2617 $pick_object
2618 && $this->setup_related_objects()
2619 && isset( self::$related_objects[ $pick_object ] )
2620 && isset( self::$related_objects[ $pick_object ]['data_callback'] )
2621 && is_callable( self::$related_objects[ $pick_object ]['data_callback'] )
2622 ) {
2623 $data = call_user_func_array(
2624 self::$related_objects[ $options[ static::$type . '_object' ] ]['data_callback'], [
2625 $name,
2626 $value,
2627 $options,
2628 $pod,
2629 $id,
2630 ]
2631 );
2632
2633 if ( 'data' === $context ) {
2634 pods_static_cache_set( $name . '/' . $options['id'], [
2635 'autocomplete' => false,
2636 ], __CLASS__ . '/field_data' );
2637 }
2638
2639 $simple = true;
2640
2641 // Cache data from callback.
2642 if ( ! empty( $data ) ) {
2643 self::$related_objects[ $options[ static::$type . '_object' ] ]['data'] = $data;
2644 }
2645 } elseif ( 'simple_value' !== $context ) {
2646 $pick_val = pods_v( static::$type . '_val', $options );
2647
2648 if ( 'table' === $pick_object ) {
2649 $pick_val = pods_v( static::$type . '_table', $options, $pick_val, true );
2650 }
2651
2652 $current_pod = null;
2653
2654 if ( $pod instanceof Pod ) {
2655 $current_pod = $pod;
2656 } elseif ( $pod instanceof Pods ) {
2657 $current_pod = $pod->pod_data;
2658 } elseif ( is_array( $pod ) ) {
2659 $current_pod = $pod;
2660 }
2661
2662 $related_pod = null;
2663
2664 if ( '__current__' === $pick_val ) {
2665 if ( $pod instanceof Pod ) {
2666 $pick_val = $pod->get_name();
2667
2668 $related_pod = $pod;
2669 } elseif ( $pod instanceof Pods ) {
2670 $pick_val = $pod->pod_data->get_name();
2671
2672 $related_pod = $pod->pod_data;
2673 } elseif ( is_array( $pod ) ) {
2674 $pick_val = $pod['name'];
2675 } elseif ( is_string( $pod ) && 0 < strlen( $pod ) ) {
2676 $pick_val = $pod;
2677 }
2678 }
2679
2680 $table_info = pods_v( 'table_info', $options );
2681
2682 if ( empty( $table_info ) && ! empty( $pick_object ) ) {
2683 $table_info = pods_api()->get_table_info( $pick_object, $pick_val, null, null, $options );
2684 }
2685
2686 // If the ACT was not found, return nothing.
2687 if ( 'pod' === $pick_object && $table_info && ! $table_info['pod'] ) {
2688 return [];
2689 }
2690
2691 if ( null === $related_pod && $table_info && $table_info['pod'] ) {
2692 $related_pod = $table_info['pod'];
2693 }
2694
2695 $search_data = pods_data( $related_pod );
2696 $search_data->table( $table_info );
2697
2698 $default_field_index = $search_data->field_index;
2699
2700 $params = [
2701 'select' => "`t`.`{$search_data->field_id}`, `t`.`{$search_data->field_index}`",
2702 'table' => $search_data->table,
2703 'where' => pods_v( static::$type . '_where', $options, null, true ),
2704 'orderby' => pods_v( static::$type . '_orderby', $options, null, true ),
2705 'having' => pods_v( static::$type . '_having', $options, null, true ),
2706 'groupby' => pods_v( static::$type . '_groupby', $options, null, true ),
2707 'pagination' => false,
2708 'search' => false,
2709 ];
2710
2711 if ( ! pods_can_use_dynamic_feature_sql_clauses( 'simple' ) ) {
2712 $params['where'] = $params['where'] ? '0=1 /* Dynamic SQL clauses disabled in Pods */' : (array) $table_info['where_default'];
2713 $params['orderby'] = null;
2714 }
2715
2716 if ( ! pods_can_use_dynamic_feature_sql_clauses( 'all' ) ) {
2717 $params['having'] = null;
2718 $params['groupby'] = null;
2719 }
2720
2721 if ( null === $params['where'] ) {
2722 $params['where'] = (array) $table_info['where_default'];
2723 }
2724
2725 if ( in_array( $options[ static::$type . '_object' ], [ 'site', 'network' ], true ) ) {
2726 $params['select'] .= ', `t`.`path`';
2727 }
2728
2729 if ( ! empty( $params['where'] ) && (array) $table_info['where_default'] !== $params['where'] ) {
2730 $params['where'] = pods_evaluate_tags( $params['where'], true );
2731 }
2732
2733 if ( ! empty( $params['having'] ) ) {
2734 $params['having'] = pods_evaluate_tags( $params['having'], true );
2735 }
2736
2737 if ( empty( $params['where'] ) || ( ! is_array( $params['where'] ) && '' === trim( $params['where'] ) ) ) {
2738 $params['where'] = [];
2739 }
2740
2741 $params['where'] = (array) $params['where'];
2742
2743 if ( 'value_to_label' === $context ) {
2744 $params['where'][] = "`t`.`{$search_data->field_id}` = " . number_format( $value, 0, '', '' );
2745 }
2746
2747 // Check if we need to limit by the current post author.
2748 if (
2749 1 === (int) pods_v( static::$type . '_post_author', $options, 0 )
2750 && $current_pod
2751 && 'post_type' === $current_pod['type']
2752 && 'post_type' === $options[ static::$type . '_object' ]
2753 ) {
2754 $post_author_id = 0;
2755
2756 if ( empty( $id ) ) {
2757 if ( is_user_logged_in() ) {
2758 $post_author_id = get_current_user_id();
2759 }
2760 } else {
2761 $post = get_post( $id );
2762
2763 if ( $post ) {
2764 $post_author_id = $post->post_author;
2765 }
2766 }
2767
2768 $params['where'][] = '`t`.`post_author` = ' . (int) $post_author_id;
2769 }
2770
2771 $display = trim( (string) pods_v( static::$type . '_display', $options ), ' {@}' );
2772
2773 $display_field = "`t`.`{$search_data->field_index}`";
2774 $display_field_name = $search_data->field_index;
2775 $display_field_alias = false;
2776
2777 if ( is_string( $display ) && 0 < strlen( $display ) ) {
2778 if ( ! empty( $table_info['pod'] ) ) {
2779 /** @var Pod $related_pod */
2780 $related_pod = $table_info['pod'];
2781
2782 $related_storage = $related_pod->get_storage();
2783 $related_type = $related_pod->get_type();
2784 $found_display_field = $related_pod->get_field( $display );
2785
2786 if ( $found_display_field ) {
2787 $display_field_name = $found_display_field->get_name();
2788 }
2789
2790 if ( $found_display_field instanceof Object_Field ) {
2791 $display_field = "`t`.`{$display_field_name}`";
2792 } elseif (
2793 'table' === $related_storage
2794 && ! in_array(
2795 $related_type, [
2796 'pod',
2797 'table',
2798 ], true
2799 )
2800 ) {
2801 $display_field = "`d`.`{$display_field_name}`";
2802 } elseif ( 'meta' === $related_storage ) {
2803 $display_field = "`{$display_field_name}`.`meta_value`";
2804
2805 $display_field_alias = true;
2806 } else {
2807 $display_field = "`t`.`{$display_field_name}`";
2808 }
2809 } elseif ( isset( $table_info['object_fields'] ) && isset( $table_info['object_fields'][ $display ]['name'] ) ) {
2810 $display_field_name = $table_info['object_fields'][ $display ]['name'];
2811
2812 $display_field = "`t`.`{$display_field_name}`";
2813 }//end if
2814 }//end if
2815
2816 if ( false === strpos( $params['select'], $display_field ) ) {
2817 $params['select'] .= ', ' . $display_field . ( $display_field_alias ? " AS `{$display_field_name}`" : '' );
2818 }
2819
2820 $autocomplete = $this->is_autocomplete( $options );
2821
2822 $hierarchy = false;
2823
2824 if ( 'data' === $context && ! $autocomplete ) {
2825 if ( 'single' === pods_v( static::$type . '_format_type', $options, 'single' ) && in_array(
2826 pods_v( static::$type . '_format_single', $options, 'dropdown' ), [
2827 'dropdown',
2828 'radio',
2829 ], true
2830 )
2831 ) {
2832 $hierarchy = true;
2833 } elseif ( 'multi' === pods_v( static::$type . '_format_type', $options, 'single' ) && in_array(
2834 pods_v( static::$type . '_format_multi', $options, 'checkbox' ), [
2835 'multiselect',
2836 'checkbox',
2837 ], true
2838 )
2839 ) {
2840 $hierarchy = true;
2841 }
2842 }
2843
2844 if ( $hierarchy && $table_info['object_hierarchical'] && ! empty( $table_info['field_parent'] ) ) {
2845 if ( false === strpos( $params['select'], $table_info['field_parent_select'] ) ) {
2846 $params['select'] .= ', ' . $table_info['field_parent_select'];
2847 }
2848 }
2849
2850 if ( $autocomplete ) {
2851 if ( 0 === $limit ) {
2852 $limit = 30;
2853 }
2854
2855 $autocomplete_limit = pods_get_setting( 'autocomplete_limit' );
2856
2857 if ( 'admin_ajax_relationship' === $context && ! empty( $data_params['query'] ) && null !== $autocomplete_limit ) {
2858 $limit = max( (int) $autocomplete_limit, - 1 );
2859
2860 if ( 0 === $limit ) {
2861 $limit = - 1;
2862 }
2863 }
2864
2865 $params['limit'] = apply_filters( 'pods_form_ui_field_pick_autocomplete_limit', $limit, $name, $value, $options, $pod, $id, $object_params );
2866
2867 if ( - 1 !== (int) $params['limit'] && is_array( $value ) && $params['limit'] < count( $value ) ) {
2868 $params['limit'] = count( $value );
2869 }
2870
2871 $params['page'] = $page;
2872
2873 if ( 'admin_ajax_relationship' === $context ) {
2874 $query_sanitized_like = pods_sanitize_like( $data_params['query'] );
2875
2876 $lookup_where = [
2877 $search_data->field_index => "`t`.`{$search_data->field_index}` LIKE '%{$query_sanitized_like}%'",
2878 ];
2879
2880 if ( $display_field_name !== $search_data->field_index ) {
2881 $lookup_where[ $display_field_name ] = "{$display_field} LIKE '%{$query_sanitized_like}%'";
2882 }
2883
2884 // @todo Hook into WPML for each table
2885 if ( $wpdb->users === $search_data->table ) {
2886 $lookup_where['display_name'] = "`t`.`display_name` LIKE '%{$query_sanitized_like}%'";
2887 $lookup_where['user_login'] = "`t`.`user_login` LIKE '%{$query_sanitized_like}%'";
2888 $lookup_where['user_email'] = "`t`.`user_email` LIKE '%{$query_sanitized_like}%'";
2889 } elseif ( $wpdb->posts === $search_data->table ) {
2890 $lookup_where['post_title'] = "`t`.`post_title` LIKE '%{$query_sanitized_like}%'";
2891 $lookup_where['post_name'] = "`t`.`post_name` LIKE '%{$query_sanitized_like}%'";
2892 $lookup_where['post_content'] = "`t`.`post_content` LIKE '%{$query_sanitized_like}%'";
2893 $lookup_where['post_excerpt'] = "`t`.`post_excerpt` LIKE '%{$query_sanitized_like}%'";
2894 } elseif ( $wpdb->terms === $search_data->table ) {
2895 $lookup_where['name'] = "`t`.`name` LIKE '%{$query_sanitized_like}%'";
2896 $lookup_where['slug'] = "`t`.`slug` LIKE '%{$query_sanitized_like}%'";
2897 } elseif ( $wpdb->comments === $search_data->table ) {
2898 $lookup_where['comment_content'] = "`t`.`comment_content` LIKE '%{$query_sanitized_like}%'";
2899 $lookup_where['comment_author'] = "`t`.`comment_author` LIKE '%{$query_sanitized_like}%'";
2900 $lookup_where['comment_author_email'] = "`t`.`comment_author_email` LIKE '%{$query_sanitized_like}%'";
2901 }
2902
2903 $lookup_where = apply_filters( 'pods_form_ui_field_pick_autocomplete_lookup', $lookup_where, $data_params['query'], $name, $value, $options, $pod, $id, $object_params, $search_data );
2904
2905 if ( ! empty( $lookup_where ) ) {
2906 $params['where'][] = implode( ' OR ', $lookup_where );
2907 }
2908
2909 $orderby = [];
2910 $orderby[] = "( {$display_field} LIKE '%{$query_sanitized_like}%' ) DESC";
2911
2912 $pick_orderby = pods_v( static::$type . '_orderby', $options, null, true );
2913
2914 if ( ! pods_can_use_dynamic_feature_sql_clauses( 'simple' ) ) {
2915 $pick_orderby = null;
2916 }
2917
2918 if ( is_string( $pick_orderby ) && 0 < strlen( $pick_orderby ) ) {
2919 $orderby[] = $pick_orderby;
2920 }
2921
2922 if ( ! in_array( $search_data->field_index, $orderby, true ) ) {
2923 $orderby[] = "`t`.`{$search_data->field_index}`";
2924 }
2925
2926 if ( ! in_array( $search_data->field_id, $orderby, true ) ) {
2927 $orderby[] = "`t`.`{$search_data->field_id}`";
2928 }
2929
2930 $params['orderby'] = $orderby;
2931 }//end if
2932 } elseif ( 0 < $limit ) {
2933 $params['limit'] = $limit;
2934 $params['page'] = $page;
2935 }//end if
2936
2937 $extra = '';
2938
2939 if ( $wpdb->posts === $search_data->table ) {
2940 $extra = '`t`.`post_type`, `t`.`menu_order`, `t`.`post_date`';
2941 } elseif ( $wpdb->terms === $search_data->table ) {
2942 $extra = '`tt`.`taxonomy`';
2943 } elseif ( $wpdb->comments === $search_data->table ) {
2944 $extra = '`t`.`comment_type`';
2945 } elseif ( $wpdb->site === $search_data->table ) {
2946 $extra = '`t`.`path`';
2947 } elseif ( $wpdb->blogs === $search_data->table ) {
2948 $extra = '`t`.`path`';
2949 }
2950
2951 if ( '' !== $extra && false === strpos( $params['select'], $extra ) ) {
2952 $params['select'] .= ', ' . $extra;
2953 }
2954
2955 if ( 'user' === pods_v( static::$type . '_object', $options ) ) {
2956 $roles = pods_v( static::$type . '_user_role', $options );
2957
2958 if ( ! empty( $roles ) ) {
2959 $where = [];
2960
2961 foreach ( (array) $roles as $role ) {
2962 if ( empty( $role ) || ( pods_clean_name( $role ) !== $role && sanitize_title( $role ) !== $role ) ) {
2963 continue;
2964 }
2965
2966 $role_sanitized_like = pods_sanitize_like( $role );
2967
2968 $where[] = "{$wpdb->prefix}capabilities.meta_value LIKE '%\"{$role_sanitized_like}\"%'";
2969 }
2970
2971 if ( ! empty( $where ) ) {
2972 $params['where'][] = implode( ' OR ', $where );
2973 }
2974 }//end if
2975 }//end if
2976
2977 if ( empty( $params['where'] ) ) {
2978 $params['where'] = null;
2979 }
2980
2981 try {
2982 $results = $search_data->select( $params );
2983 } catch ( Exception $exception ) {
2984 if ( pods_is_debug_display() ) {
2985 pods_error_exception( $exception );
2986 }
2987
2988 $results = [];
2989 }
2990
2991 if ( $autocomplete && 0 < $params['limit'] && $params['limit'] < $search_data->total_found() ) {
2992 if ( ! empty( $value ) ) {
2993 $ids = $value;
2994
2995 if ( is_array( $ids ) ) {
2996 if ( isset( $ids[0] ) && is_array( $ids[0] ) ) {
2997 $ids = wp_list_pluck( $ids, $search_data->field_id );
2998 }
2999
3000 if ( $params['limit'] < count( $ids ) ) {
3001 $params['limit'] = count( $ids );
3002 }
3003
3004 $ids = array_map( 'absint', $ids );
3005 $ids = implode( ', ', $ids );
3006 } else {
3007 $ids = (int) $ids;
3008 }
3009
3010 if ( is_array( $params['where'] ) ) {
3011 $params['where'] = implode( ' AND ', $params['where'] );
3012 }
3013 if ( ! empty( $params['where'] ) ) {
3014 $params['where'] = '(' . $params['where'] . ') AND ';
3015 }
3016
3017 $params['where'] .= "`t`.`{$search_data->field_id}` IN ( {$ids} )";
3018
3019 try {
3020 $results = array_merge( $results, $search_data->select( $params ) );
3021 } catch ( Exception $exception ) {
3022 if ( pods_is_debug_display() ) {
3023 pods_error_exception( $exception );
3024 }
3025
3026 $results = [];
3027 }
3028 }//end if
3029 } else {
3030 $autocomplete = false;
3031 }//end if
3032
3033 if ( 'data' === $context ) {
3034 pods_static_cache_set( $name . '/' . $options['id'], [
3035 'autocomplete' => $autocomplete,
3036 ], __CLASS__ . '/field_data' );
3037 }
3038
3039 if ( $hierarchy && ! $autocomplete && ! empty( $results ) && $table_info['object_hierarchical'] && ! empty( $table_info['field_parent'] ) ) {
3040 $select_args = [
3041 'id' => $table_info['field_id'],
3042 'index' => $display_field_name,
3043 'parent' => $table_info['field_parent'],
3044 ];
3045
3046 $results = pods_hierarchical_select( $results, $select_args );
3047 }
3048
3049 $ids = [];
3050
3051 if ( ! empty( $results ) ) {
3052 foreach ( $results as $result ) {
3053 $result = get_object_vars( $result );
3054 $field_id = $search_data->field_id;
3055 $field_index = $display_field_name;
3056
3057 if ( ! isset( $result[ $field_index ] ) ) {
3058 $field_index = $default_field_index;
3059 }
3060
3061 if ( ! isset( $result[ $field_id ], $result[ $field_index ] ) ) {
3062 continue;
3063 }
3064
3065 $result[ $field_index ] = trim( $result[ $field_index ] );
3066
3067 $object = '';
3068 $object_type = '';
3069
3070 if ( $wpdb->posts === $search_data->table && isset( $result['post_type'] ) ) {
3071 $object = $result['post_type'];
3072 $object_type = 'post_type';
3073 } elseif ( $wpdb->terms === $search_data->table && isset( $result['taxonomy'] ) ) {
3074 $object = $result['taxonomy'];
3075 $object_type = 'taxonomy';
3076 }
3077
3078 $field_index_data_to_use = pods_v( $field_index, $search_data->object_fields );
3079
3080 $display_filter = pods_v( 'display_filter', $field_index_data_to_use );
3081
3082 if ( is_string( $display_filter ) && 0 < strlen( $display_filter ) ) {
3083 $display_filter_args = pods_v( 'display_filter_args', $field_index_data_to_use );
3084
3085 $filter_args = [
3086 $display_filter,
3087 $result[ $field_index ],
3088 ];
3089
3090 if ( ! empty( $display_filter_args ) ) {
3091 foreach ( (array) $display_filter_args as $display_filter_arg ) {
3092 // Manual solution to a problem that won't map correctly.
3093 if ( 'post_ID' === $display_filter_arg ) {
3094 $display_filter_arg = 'ID';
3095 }
3096
3097 if ( isset( $result[ $display_filter_arg ] ) ) {
3098 $filter_args[] = $result[ $display_filter_arg ];
3099 }
3100 }
3101 }
3102
3103 $result[ $field_index ] = call_user_func_array( 'apply_filters', $filter_args );
3104 }
3105
3106 if ( in_array( $options[ static::$type . '_object' ], [ 'site', 'network' ], true ) ) {
3107 $result[ $field_index ] = $result[ $field_index ] . $result['path'];
3108 } elseif ( '' === $result[ $field_index ] ) {
3109 $result[ $field_index ] = '(No Title)';
3110 }
3111
3112 if ( 'admin_ajax_relationship' === $context ) {
3113 $items[] = $this->build_dfv_field_item_data_recurse_item( $result[ $field_id ], $result[ $field_index ], (object) $object_params );
3114 } else {
3115 $data[ $result[ $field_id ] ] = $result[ $field_index ];
3116 }
3117
3118 $ids[] = $result[ $field_id ];
3119 }//end foreach
3120 }//end if
3121 }//end if
3122
3123 if ( $simple && 'admin_ajax_relationship' === $context ) {
3124 $found_data = [];
3125
3126 foreach ( $data as $k => $v ) {
3127 if ( false !== stripos( $v, $data_params['query'] ) || false !== stripos( $k, $data_params['query'] ) ) {
3128 $found_data[ $k ] = $v;
3129 }
3130 }
3131
3132 $data = $found_data;
3133 }
3134 }//end if
3135
3136 if ( 'admin_ajax_relationship' === $context ) {
3137 if ( empty( $items ) && ! empty( $data ) ) {
3138 foreach ( $data as $k => $v ) {
3139 $items[] = [
3140 'id' => $k,
3141 'text' => $v,
3142 ];
3143 }
3144 }
3145
3146 $data = $items;
3147 }
3148
3149 return $data;
3150
3151 }
3152
3153 /**
3154 * Check if field is autocomplete enabled.
3155 *
3156 * @param array $options Field options.
3157 *
3158 * @return bool
3159 *
3160 * @since 2.7.0
3161 */
3162 private function is_autocomplete( $options ) {
3163
3164 $autocomplete = false;
3165
3166 if ( 'single' === pods_v( static::$type . '_format_type', $options, 'single' ) ) {
3167 if ( in_array( pods_v( static::$type . '_format_single', $options, 'dropdown' ), [
3168 'autocomplete',
3169 'list',
3170 ], true ) ) {
3171 $autocomplete = true;
3172 }
3173 } elseif ( 'multi' === pods_v( static::$type . '_format_type', $options, 'single' ) ) {
3174 if ( in_array( pods_v( static::$type . '_format_multi', $options, 'checkbox' ), [
3175 'autocomplete',
3176 'list',
3177 ], true ) ) {
3178 $autocomplete = true;
3179 }
3180 }
3181
3182 return $autocomplete;
3183 }
3184
3185 /**
3186 * Check if a field type is a tableless text field type.
3187 *
3188 * @since 2.7.4
3189 *
3190 * @param string $type Field type.
3191 * @param array $options Field options.
3192 *
3193 * @return bool True if the field type is a tableless text field type, false otherwise.
3194 */
3195 private function is_simple_tableless( $type, array $options ) {
3196 $field_object = pods_v( $type . '_object', $options );
3197
3198 return in_array( $field_object, PodsForm::simple_tableless_objects(), true );
3199 }
3200
3201 /**
3202 * Check if a field supports AJAX mode
3203 *
3204 * @param string $type Field type.
3205 * @param array $options Field options.
3206 *
3207 * @return bool
3208 * @since 2.7.4
3209 */
3210 private function can_ajax( $type, $options ) {
3211 return $this->is_autocomplete( $options ) && ! $this->is_simple_tableless( $type, $options );
3212 }
3213
3214
3215 /**
3216 * Handle autocomplete AJAX.
3217 *
3218 * @since 2.3.0
3219 */
3220 public function admin_ajax_relationship() {
3221
3222 pods_session_start();
3223
3224 // Sanitize input.
3225 // @codingStandardsIgnoreLine
3226 $params = pods_unslash( (array) $_POST );
3227
3228 foreach ( $params as $key => $value ) {
3229 if ( 'action' === $key ) {
3230 continue;
3231 }
3232
3233 unset( $params[ $key ] );
3234
3235 $params[ str_replace( '_podsfix_', '', $key ) ] = $value;
3236 }
3237
3238 $params = (object) $params;
3239
3240 if ( ! isset( $params->_wpnonce, $params->pod_name, $params->field_name, $params->uri_hash, $params->id ) ) {
3241 pods_error( __( 'Unauthorized request', 'pods' ), PodsInit::$admin );
3242 }
3243
3244 if ( ! isset( $params->query ) || '' === trim( $params->query ) ) {
3245 pods_error( __( 'Invalid field request', 'pods' ), PodsInit::$admin );
3246 }
3247
3248 $_wpnonce = $params->_wpnonce;
3249 $pod_name = $params->pod_name;
3250 $field_name = $params->field_name;
3251 $uri_hash = $params->uri_hash;
3252 $id = (int) $params->id;
3253
3254 $query = $params->query;
3255 $limit = pods_v( 'limit', $params, 15, true );
3256 $page = pods_v( 'page', $params, 1, true );
3257
3258 $uid = pods_session_id();
3259
3260 if ( is_user_logged_in() ) {
3261 $uid = 'user_' . get_current_user_id();
3262 }
3263
3264 $nonce_name = 'pods_relationship:' . json_encode( compact( 'pod_name', 'field_name', 'uid', 'uri_hash', 'id' ) );
3265
3266 if ( false === wp_verify_nonce( $_wpnonce, $nonce_name ) ) {
3267 pods_error( __( 'Unauthorized request', 'pods' ), PodsInit::$admin );
3268 }
3269
3270 if ( empty( self::$api ) ) {
3271 self::$api = pods_api();
3272 }
3273
3274 $pod = self::$api->load_pod( [
3275 'name' => $pod_name,
3276 ] );
3277
3278 if ( ! $pod ) {
3279 pods_error( __( 'Invalid Pod configuration', 'pods' ), PodsInit::$admin );
3280 }
3281
3282 $field = $pod->get_field( $field_name );
3283
3284 if ( ! $field ) {
3285 pods_error( __( 'Invalid Field configuration', 'pods' ), PodsInit::$admin );
3286 }
3287
3288 if ( ! $field->is_autocomplete_relationship() ) {
3289 pods_error( __( 'Invalid field', 'pods' ), PodsInit::$admin );
3290 }
3291
3292 $object_params = [
3293 // The name of the field.
3294 'name' => $field['name'],
3295 // The value of the field.
3296 'value' => null,
3297 // Field options.
3298 'options' => $field,
3299 // Pod data.
3300 'pod' => $pod,
3301 // Item ID.
3302 'id' => $id,
3303 // Data context.
3304 'context' => 'admin_ajax_relationship',
3305 'data_params' => $params,
3306 'page' => $page,
3307 'limit' => $limit,
3308 ];
3309
3310 $pick_data = apply_filters( 'pods_field_pick_data_ajax', null, $field['name'], null, $field, $pod, $id );
3311
3312 if ( null !== $pick_data ) {
3313 $items = $pick_data;
3314 } else {
3315 $items = $this->get_object_data( $object_params );
3316 }
3317
3318 if ( ! empty( $items ) && isset( $items[0] ) && ! is_array( $items[0] ) ) {
3319 $new_items = [];
3320
3321 foreach ( $items as $id => $text ) {
3322 $new_items[] = [
3323 'id' => $id,
3324 'text' => $text,
3325 'image' => '',
3326 ];
3327 }
3328
3329 $items = $new_items;
3330 }
3331
3332 $items = apply_filters( 'pods_field_pick_data_ajax_items', $items, $field['name'], null, $field, $pod, $id );
3333
3334 $items = [
3335 'results' => $items,
3336 ];
3337
3338 wp_send_json( $items );
3339
3340 die();
3341 // KBAI!
3342 }
3343
3344 /**
3345 * Data callback for Post Stati.
3346 *
3347 * @param string|null $name The name of the field.
3348 * @param string|array|null $value The value of the field.
3349 * @param array|null $options Field options.
3350 * @param array|null $pod Pod data.
3351 * @param int|null $id Item ID.
3352 *
3353 * @return array
3354 *
3355 * @since 2.3.0
3356 */
3357 public function data_post_stati( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
3358 $data = [];
3359
3360 $post_stati = get_post_stati( [], 'objects' );
3361
3362 foreach ( $post_stati as $post_status ) {
3363 $data[ $post_status->name ] = $post_status->label;
3364 }
3365
3366 return (array) apply_filters( 'pods_form_ui_field_pick_data_post_stati', $data, $name, $value, $options, $pod, $id );
3367 }
3368
3369 /**
3370 * Data callback for Post Stati (with any).
3371 *
3372 * @param string|null $name The name of the field.
3373 * @param string|array|null $value The value of the field.
3374 * @param array|null $options Field options.
3375 * @param array|null $pod Pod data.
3376 * @param int|null $id Item ID.
3377 *
3378 * @return array
3379 *
3380 * @since 2.9.10
3381 */
3382 public function data_post_stati_with_any( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
3383 $data = [
3384 '_pods_any' => esc_html__( 'Any Status (excluding Auto-Draft and Trashed)', 'pods' ),
3385 ];
3386
3387 $data = array_merge( $data, $this->data_post_stati( $name, $value, $options, $pod, $id ) );
3388
3389 /**
3390 * Allow filtering the list of post stati with any.
3391 *
3392 * @since 2.9.10
3393 *
3394 * @param array $data The list of post stati with any.
3395 * @param string|null $name The name of the field.
3396 * @param string|array|null $value The value of the field.
3397 * @param array|null $options Field options.
3398 * @param array|null $pod Pod data.
3399 * @param int|null $id Item ID.
3400 */
3401 return (array) apply_filters( 'pods_form_ui_field_pick_data_post_stati_with_any', $data, $name, $value, $options, $pod, $id );
3402 }
3403
3404 /**
3405 * Data callback for User Roles.
3406 *
3407 * @param string|null $name The name of the field.
3408 * @param string|array|null $value The value of the field.
3409 * @param array|null $options Field options.
3410 * @param array|null $pod Pod data.
3411 * @param int|null $id Item ID.
3412 *
3413 * @return array
3414 *
3415 * @since 2.3.0
3416 */
3417 public function data_roles( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
3418
3419 $data = [];
3420
3421 global $wp_roles;
3422
3423 foreach ( $wp_roles->role_objects as $key => $role ) {
3424 $data[ $key ] = $wp_roles->role_names[ $key ];
3425 }
3426
3427 return apply_filters( 'pods_form_ui_field_pick_data_roles', $data, $name, $value, $options, $pod, $id );
3428
3429 }
3430
3431 /**
3432 * Data callback for User Capabilities.
3433 *
3434 * @param string|null $name The name of the field.
3435 * @param string|array|null $value The value of the field.
3436 * @param array|null $options Field options.
3437 * @param array|null $pod Pod data.
3438 * @param int|null $id Item ID.
3439 *
3440 * @return array
3441 *
3442 * @since 2.3.0
3443 */
3444 public function data_capabilities( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
3445
3446 $data = [];
3447
3448 global $wp_roles;
3449
3450 $default_caps = [
3451 'activate_plugins',
3452 'add_users',
3453 'create_users',
3454 'delete_others_pages',
3455 'delete_others_posts',
3456 'delete_pages',
3457 'delete_plugins',
3458 'delete_posts',
3459 'delete_private_pages',
3460 'delete_private_posts',
3461 'delete_published_pages',
3462 'delete_published_posts',
3463 'delete_users',
3464 'edit_dashboard',
3465 'edit_files',
3466 'edit_others_pages',
3467 'edit_others_posts',
3468 'edit_pages',
3469 'edit_plugins',
3470 'edit_posts',
3471 'edit_private_pages',
3472 'edit_private_posts',
3473 'edit_published_pages',
3474 'edit_published_posts',
3475 'edit_theme_options',
3476 'edit_themes',
3477 'edit_users',
3478 'import',
3479 'install_plugins',
3480 'install_themes',
3481 'list_users',
3482 'manage_categories',
3483 'manage_links',
3484 'manage_options',
3485 'moderate_comments',
3486 'promote_users',
3487 'publish_pages',
3488 'publish_posts',
3489 'read',
3490 'read_private_pages',
3491 'read_private_posts',
3492 'remove_users',
3493 'switch_themes',
3494 'unfiltered_html',
3495 'unfiltered_upload',
3496 'update_core',
3497 'update_plugins',
3498 'update_themes',
3499 'upload_files',
3500 ];
3501
3502 $role_caps = [];
3503
3504 foreach ( $wp_roles->role_objects as $key => $role ) {
3505 if ( is_array( $role->capabilities ) ) {
3506 foreach ( $role->capabilities as $cap => $grant ) {
3507 $role_caps[ $cap ] = $cap;
3508 }
3509 }
3510 }
3511
3512 $role_caps = array_unique( $role_caps );
3513
3514 $capabilities = array_merge( $default_caps, $role_caps );
3515
3516 // To support Members filters.
3517 $capabilities = apply_filters( 'members_get_capabilities', $capabilities ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
3518
3519 $capabilities = apply_filters( 'pods_roles_get_capabilities', $capabilities );
3520
3521 sort( $capabilities );
3522
3523 $capabilities = array_unique( $capabilities );
3524
3525 global $wp_roles;
3526
3527 foreach ( $capabilities as $capability ) {
3528 $data[ $capability ] = $capability;
3529 }
3530
3531 return apply_filters( 'pods_form_ui_field_pick_data_capabilities', $data, $name, $value, $options, $pod, $id );
3532
3533 }
3534
3535 /**
3536 * Data callback for Image Sizes.
3537 *
3538 * @param string|null $name The name of the field.
3539 * @param string|array|null $value The value of the field.
3540 * @param array|null $options Field options.
3541 * @param array|null $pod Pod data.
3542 * @param int|null $id Item ID.
3543 *
3544 * @return array
3545 *
3546 * @since 2.3.0
3547 */
3548 public function data_image_sizes( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
3549
3550 $data = [];
3551
3552 $image_sizes = get_intermediate_image_sizes();
3553
3554 foreach ( $image_sizes as $image_size ) {
3555 $data[ $image_size ] = ucwords( str_replace( '-', ' ', $image_size ) );
3556 }
3557
3558 return apply_filters( 'pods_form_ui_field_pick_data_image_sizes', $data, $name, $value, $options, $pod, $id );
3559
3560 }
3561
3562 /**
3563 * Data callback for Post Types
3564 *
3565 * @param string $name The name of the field
3566 * @param string|array $value The value of the field
3567 * @param array $options Field options
3568 * @param array $pod Pod data
3569 * @param int $id Item ID
3570 *
3571 * @return array
3572 *
3573 * @since 2.3.0
3574 */
3575 public function data_post_types( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
3576
3577 $data = [];
3578
3579 $post_types = get_post_types( [], 'objects' );
3580
3581 $ignore = [
3582 'revision',
3583 'nav_menu_item',
3584 'custom_css',
3585 'customize_changeset',
3586 'attachment',
3587 'oembed_cache',
3588 'user_request',
3589 'wp_block',
3590 'wp_template',
3591 'wp_template_part',
3592 'wp_global_styles',
3593 'wp_navigation',
3594 ];
3595
3596 foreach ( $post_types as $post_type ) {
3597 if ( in_array( $post_type->name, $ignore, true ) || 0 === strpos( $post_type->name, '_pods_' ) ) {
3598 continue;
3599 }
3600
3601 $data[ $post_type->name ] = $post_type->label;
3602 }
3603
3604 return apply_filters( 'pods_form_ui_field_pick_data_post_types', $data, $name, $value, $options, $pod, $id );
3605 }
3606
3607 /**
3608 * Data callback for Taxonomies
3609 *
3610 * @param string $name The name of the field
3611 * @param string|array $value The value of the field
3612 * @param array $options Field options
3613 * @param array $pod Pod data
3614 * @param int $id Item ID
3615 *
3616 * @return array
3617 *
3618 * @since 2.3.0
3619 */
3620 public function data_taxonomies( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
3621
3622 $data = [];
3623
3624 $taxonomies = get_taxonomies( [], 'objects' );
3625
3626 $ignore = [ 'nav_menu', 'post_format' ];
3627
3628 foreach ( $taxonomies as $taxonomy ) {
3629 if ( in_array( $taxonomy->name, $ignore, true ) ) {
3630 continue;
3631 }
3632
3633 $data[ $taxonomy->name ] = $taxonomy->label;
3634 }
3635
3636 return apply_filters( 'pods_form_ui_field_pick_data_taxonomies', $data, $name, $value, $options, $pod, $id );
3637 }
3638
3639 /**
3640 * Data callback for Countries.
3641 *
3642 * @param string|null $name The name of the field.
3643 * @param string|array|null $value The value of the field.
3644 * @param array|null $options Field options.
3645 * @param array|null $pod Pod data.
3646 * @param int|null $id Item ID.
3647 *
3648 * @return array
3649 *
3650 * @since 2.3.0
3651 */
3652 public function data_countries( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
3653
3654 $data = [
3655 'AF' => __( 'Afghanistan', 'pods' ),
3656 'AL' => __( 'Albania', 'pods' ),
3657 'DZ' => __( 'Algeria', 'pods' ),
3658 'AS' => __( 'American Samoa', 'pods' ),
3659 'AD' => __( 'Andorra', 'pods' ),
3660 'AO' => __( 'Angola', 'pods' ),
3661 'AI' => __( 'Anguilla', 'pods' ),
3662 'AQ' => __( 'Antarctica', 'pods' ),
3663 'AG' => __( 'Antigua and Barbuda', 'pods' ),
3664 'AR' => __( 'Argentina', 'pods' ),
3665 'AM' => __( 'Armenia', 'pods' ),
3666 'AW' => __( 'Aruba', 'pods' ),
3667 'AU' => __( 'Australia', 'pods' ),
3668 'AT' => __( 'Austria', 'pods' ),
3669 'AZ' => __( 'Azerbaijan', 'pods' ),
3670 'BS' => __( 'Bahamas', 'pods' ),
3671 'BH' => __( 'Bahrain', 'pods' ),
3672 'BD' => __( 'Bangladesh', 'pods' ),
3673 'BB' => __( 'Barbados', 'pods' ),
3674 'BY' => __( 'Belarus', 'pods' ),
3675 'BE' => __( 'Belgium', 'pods' ),
3676 'BZ' => __( 'Belize', 'pods' ),
3677 'BJ' => __( 'Benin', 'pods' ),
3678 'BM' => __( 'Bermuda', 'pods' ),
3679 'BT' => __( 'Bhutan', 'pods' ),
3680 'BO' => __( 'Bolivia', 'pods' ),
3681 'BA' => __( 'Bosnia and Herzegovina', 'pods' ),
3682 'BW' => __( 'Botswana', 'pods' ),
3683 'BV' => __( 'Bouvet Island', 'pods' ),
3684 'BR' => __( 'Brazil', 'pods' ),
3685 'BQ' => __( 'British Antarctic Territory', 'pods' ),
3686 'IO' => __( 'British Indian Ocean Territory', 'pods' ),
3687 'VG' => __( 'British Virgin Islands', 'pods' ),
3688 'BN' => __( 'Brunei', 'pods' ),
3689 'BG' => __( 'Bulgaria', 'pods' ),
3690 'BF' => __( 'Burkina Faso', 'pods' ),
3691 'BI' => __( 'Burundi', 'pods' ),
3692 'KH' => __( 'Cambodia', 'pods' ),
3693 'CM' => __( 'Cameroon', 'pods' ),
3694 'CA' => __( 'Canada', 'pods' ),
3695 'CT' => __( 'Canton and Enderbury Islands', 'pods' ),
3696 'CV' => __( 'Cape Verde', 'pods' ),
3697 'KY' => __( 'Cayman Islands', 'pods' ),
3698 'CF' => __( 'Central African Republic', 'pods' ),
3699 'TD' => __( 'Chad', 'pods' ),
3700 'CL' => __( 'Chile', 'pods' ),
3701 'CN' => __( 'China', 'pods' ),
3702 'CX' => __( 'Christmas Island', 'pods' ),
3703 'CC' => __( 'Cocos [Keeling] Islands', 'pods' ),
3704 'CO' => __( 'Colombia', 'pods' ),
3705 'KM' => __( 'Comoros', 'pods' ),
3706 'CG' => __( 'Congo - Brazzaville', 'pods' ),
3707 'CD' => __( 'Congo - Kinshasa', 'pods' ),
3708 'CK' => __( 'Cook Islands', 'pods' ),
3709 'CR' => __( 'Costa Rica', 'pods' ),
3710 'HR' => __( 'Croatia', 'pods' ),
3711 'CU' => __( 'Cuba', 'pods' ),
3712 'CY' => __( 'Cyprus', 'pods' ),
3713 'CZ' => __( 'Czech Republic', 'pods' ),
3714 'CI' => __( 'Côte d’Ivoire', 'pods' ),
3715 'DK' => __( 'Denmark', 'pods' ),
3716 'DJ' => __( 'Djibouti', 'pods' ),
3717 'DM' => __( 'Dominica', 'pods' ),
3718 'DO' => __( 'Dominican Republic', 'pods' ),
3719 'NQ' => __( 'Dronning Maud Land', 'pods' ),
3720 'DD' => __( 'East Germany', 'pods' ),
3721 'EC' => __( 'Ecuador', 'pods' ),
3722 'EG' => __( 'Egypt', 'pods' ),
3723 'SV' => __( 'El Salvador', 'pods' ),
3724 'GQ' => __( 'Equatorial Guinea', 'pods' ),
3725 'ER' => __( 'Eritrea', 'pods' ),
3726 'EE' => __( 'Estonia', 'pods' ),
3727 'ET' => __( 'Ethiopia', 'pods' ),
3728 'FK' => __( 'Falkland Islands', 'pods' ),
3729 'FO' => __( 'Faroe Islands', 'pods' ),
3730 'FJ' => __( 'Fiji', 'pods' ),
3731 'FI' => __( 'Finland', 'pods' ),
3732 'FR' => __( 'France', 'pods' ),
3733 'GF' => __( 'French Guiana', 'pods' ),
3734 'PF' => __( 'French Polynesia', 'pods' ),
3735 'TF' => __( 'French Southern Territories', 'pods' ),
3736 'FQ' => __( 'French Southern and Antarctic Territories', 'pods' ),
3737 'GA' => __( 'Gabon', 'pods' ),
3738 'GM' => __( 'Gambia', 'pods' ),
3739 'GE' => __( 'Georgia', 'pods' ),
3740 'DE' => __( 'Germany', 'pods' ),
3741 'GH' => __( 'Ghana', 'pods' ),
3742 'GI' => __( 'Gibraltar', 'pods' ),
3743 'GR' => __( 'Greece', 'pods' ),
3744 'GL' => __( 'Greenland', 'pods' ),
3745 'GD' => __( 'Grenada', 'pods' ),
3746 'GP' => __( 'Guadeloupe', 'pods' ),
3747 'GU' => __( 'Guam', 'pods' ),
3748 'GT' => __( 'Guatemala', 'pods' ),
3749 'GG' => __( 'Guernsey', 'pods' ),
3750 'GN' => __( 'Guinea', 'pods' ),
3751 'GW' => __( 'Guinea-Bissau', 'pods' ),
3752 'GY' => __( 'Guyana', 'pods' ),
3753 'HT' => __( 'Haiti', 'pods' ),
3754 'HM' => __( 'Heard Island and McDonald Islands', 'pods' ),
3755 'HN' => __( 'Honduras', 'pods' ),
3756 'HK' => __( 'Hong Kong SAR China', 'pods' ),
3757 'HU' => __( 'Hungary', 'pods' ),
3758 'IS' => __( 'Iceland', 'pods' ),
3759 'IN' => __( 'India', 'pods' ),
3760 'ID' => __( 'Indonesia', 'pods' ),
3761 'IR' => __( 'Iran', 'pods' ),
3762 'IQ' => __( 'Iraq', 'pods' ),
3763 'IE' => __( 'Ireland', 'pods' ),
3764 'IM' => __( 'Isle of Man', 'pods' ),
3765 'IL' => __( 'Israel', 'pods' ),
3766 'IT' => __( 'Italy', 'pods' ),
3767 'JM' => __( 'Jamaica', 'pods' ),
3768 'JP' => __( 'Japan', 'pods' ),
3769 'JE' => __( 'Jersey', 'pods' ),
3770 'JT' => __( 'Johnston Island', 'pods' ),
3771 'JO' => __( 'Jordan', 'pods' ),
3772 'KZ' => __( 'Kazakhstan', 'pods' ),
3773 'KE' => __( 'Kenya', 'pods' ),
3774 'KI' => __( 'Kiribati', 'pods' ),
3775 'KW' => __( 'Kuwait', 'pods' ),
3776 'KG' => __( 'Kyrgyzstan', 'pods' ),
3777 'LA' => __( 'Laos', 'pods' ),
3778 'LV' => __( 'Latvia', 'pods' ),
3779 'LB' => __( 'Lebanon', 'pods' ),
3780 'LS' => __( 'Lesotho', 'pods' ),
3781 'LR' => __( 'Liberia', 'pods' ),
3782 'LY' => __( 'Libya', 'pods' ),
3783 'LI' => __( 'Liechtenstein', 'pods' ),
3784 'LT' => __( 'Lithuania', 'pods' ),
3785 'LU' => __( 'Luxembourg', 'pods' ),
3786 'MO' => __( 'Macau SAR China', 'pods' ),
3787 'MK' => __( 'Macedonia', 'pods' ),
3788 'MG' => __( 'Madagascar', 'pods' ),
3789 'MW' => __( 'Malawi', 'pods' ),
3790 'MY' => __( 'Malaysia', 'pods' ),
3791 'MV' => __( 'Maldives', 'pods' ),
3792 'ML' => __( 'Mali', 'pods' ),
3793 'MT' => __( 'Malta', 'pods' ),
3794 'MH' => __( 'Marshall Islands', 'pods' ),
3795 'MQ' => __( 'Martinique', 'pods' ),
3796 'MR' => __( 'Mauritania', 'pods' ),
3797 'MU' => __( 'Mauritius', 'pods' ),
3798 'YT' => __( 'Mayotte', 'pods' ),
3799 'FX' => __( 'Metropolitan France', 'pods' ),
3800 'MX' => __( 'Mexico', 'pods' ),
3801 'FM' => __( 'Micronesia', 'pods' ),
3802 'MI' => __( 'Midway Islands', 'pods' ),
3803 'MD' => __( 'Moldova', 'pods' ),
3804 'MC' => __( 'Monaco', 'pods' ),
3805 'MN' => __( 'Mongolia', 'pods' ),
3806 'ME' => __( 'Montenegro', 'pods' ),
3807 'MS' => __( 'Montserrat', 'pods' ),
3808 'MA' => __( 'Morocco', 'pods' ),
3809 'MZ' => __( 'Mozambique', 'pods' ),
3810 'MM' => __( 'Myanmar [Burma]', 'pods' ),
3811 'NA' => __( 'Namibia', 'pods' ),
3812 'NR' => __( 'Nauru', 'pods' ),
3813 'NP' => __( 'Nepal', 'pods' ),
3814 'NL' => __( 'Netherlands', 'pods' ),
3815 'AN' => __( 'Netherlands Antilles', 'pods' ),
3816 'NT' => __( 'Neutral Zone', 'pods' ),
3817 'NC' => __( 'New Caledonia', 'pods' ),
3818 'NZ' => __( 'New Zealand', 'pods' ),
3819 'NI' => __( 'Nicaragua', 'pods' ),
3820 'NE' => __( 'Niger', 'pods' ),
3821 'NG' => __( 'Nigeria', 'pods' ),
3822 'NU' => __( 'Niue', 'pods' ),
3823 'NF' => __( 'Norfolk Island', 'pods' ),
3824 'KP' => __( 'North Korea', 'pods' ),
3825 'VD' => __( 'North Vietnam', 'pods' ),
3826 'MP' => __( 'Northern Mariana Islands', 'pods' ),
3827 'NO' => __( 'Norway', 'pods' ),
3828 'OM' => __( 'Oman', 'pods' ),
3829 'PC' => __( 'Pacific Islands Trust Territory', 'pods' ),
3830 'PK' => __( 'Pakistan', 'pods' ),
3831 'PW' => __( 'Palau', 'pods' ),
3832 'PS' => __( 'Palestinian Territories', 'pods' ),
3833 'PA' => __( 'Panama', 'pods' ),
3834 'PZ' => __( 'Panama Canal Zone', 'pods' ),
3835 'PG' => __( 'Papua New Guinea', 'pods' ),
3836 'PY' => __( 'Paraguay', 'pods' ),
3837 'YD' => __( "People's Democratic Republic of Yemen", 'pods' ),
3838 'PE' => __( 'Peru', 'pods' ),
3839 'PH' => __( 'Philippines', 'pods' ),
3840 'PN' => __( 'Pitcairn Islands', 'pods' ),
3841 'PL' => __( 'Poland', 'pods' ),
3842 'PT' => __( 'Portugal', 'pods' ),
3843 'PR' => __( 'Puerto Rico', 'pods' ),
3844 'QA' => __( 'Qatar', 'pods' ),
3845 'RO' => __( 'Romania', 'pods' ),
3846 'RU' => __( 'Russia', 'pods' ),
3847 'RW' => __( 'Rwanda', 'pods' ),
3848 'RE' => __( 'Réunion', 'pods' ),
3849 'BL' => __( 'Saint Barthélemy', 'pods' ),
3850 'SH' => __( 'Saint Helena', 'pods' ),
3851 'KN' => __( 'Saint Kitts and Nevis', 'pods' ),
3852 'LC' => __( 'Saint Lucia', 'pods' ),
3853 'MF' => __( 'Saint Martin', 'pods' ),
3854 'PM' => __( 'Saint Pierre and Miquelon', 'pods' ),
3855 'VC' => __( 'Saint Vincent and the Grenadines', 'pods' ),
3856 'WS' => __( 'Samoa', 'pods' ),
3857 'SM' => __( 'San Marino', 'pods' ),
3858 'SA' => __( 'Saudi Arabia', 'pods' ),
3859 'SN' => __( 'Senegal', 'pods' ),
3860 'RS' => __( 'Serbia', 'pods' ),
3861 'CS' => __( 'Serbia and Montenegro', 'pods' ),
3862 'SC' => __( 'Seychelles', 'pods' ),
3863 'SL' => __( 'Sierra Leone', 'pods' ),
3864 'SG' => __( 'Singapore', 'pods' ),
3865 'SK' => __( 'Slovakia', 'pods' ),
3866 'SI' => __( 'Slovenia', 'pods' ),
3867 'SB' => __( 'Solomon Islands', 'pods' ),
3868 'SO' => __( 'Somalia', 'pods' ),
3869 'ZA' => __( 'South Africa', 'pods' ),
3870 'GS' => __( 'South Georgia and the South Sandwich Islands', 'pods' ),
3871 'KR' => __( 'South Korea', 'pods' ),
3872 'ES' => __( 'Spain', 'pods' ),
3873 'LK' => __( 'Sri Lanka', 'pods' ),
3874 'SD' => __( 'Sudan', 'pods' ),
3875 'SR' => __( 'Suriname', 'pods' ),
3876 'SJ' => __( 'Svalbard and Jan Mayen', 'pods' ),
3877 'SZ' => __( 'Swaziland', 'pods' ),
3878 'SE' => __( 'Sweden', 'pods' ),
3879 'CH' => __( 'Switzerland', 'pods' ),
3880 'SY' => __( 'Syria', 'pods' ),
3881 'ST' => __( 'São Tomé and Príncipe', 'pods' ),
3882 'TW' => __( 'Taiwan', 'pods' ),
3883 'TJ' => __( 'Tajikistan', 'pods' ),
3884 'TZ' => __( 'Tanzania', 'pods' ),
3885 'TH' => __( 'Thailand', 'pods' ),
3886 'TL' => __( 'Timor-Leste', 'pods' ),
3887 'TG' => __( 'Togo', 'pods' ),
3888 'TK' => __( 'Tokelau', 'pods' ),
3889 'TO' => __( 'Tonga', 'pods' ),
3890 'TT' => __( 'Trinidad and Tobago', 'pods' ),
3891 'TN' => __( 'Tunisia', 'pods' ),
3892 'TR' => __( 'Turkey', 'pods' ),
3893 'TM' => __( 'Turkmenistan', 'pods' ),
3894 'TC' => __( 'Turks and Caicos Islands', 'pods' ),
3895 'TV' => __( 'Tuvalu', 'pods' ),
3896 'UM' => __( 'U.S. Minor Outlying Islands', 'pods' ),
3897 'PU' => __( 'U.S. Miscellaneous Pacific Islands', 'pods' ),
3898 'VI' => __( 'U.S. Virgin Islands', 'pods' ),
3899 'UG' => __( 'Uganda', 'pods' ),
3900 'UA' => __( 'Ukraine', 'pods' ),
3901 'SU' => __( 'Union of Soviet Socialist Republics', 'pods' ),
3902 'AE' => __( 'United Arab Emirates', 'pods' ),
3903 'GB' => __( 'United Kingdom', 'pods' ),
3904 'US' => __( 'United States', 'pods' ),
3905 'ZZ' => __( 'Unknown or Invalid Region', 'pods' ),
3906 'UY' => __( 'Uruguay', 'pods' ),
3907 'UZ' => __( 'Uzbekistan', 'pods' ),
3908 'VU' => __( 'Vanuatu', 'pods' ),
3909 'VA' => __( 'Vatican City', 'pods' ),
3910 'VE' => __( 'Venezuela', 'pods' ),
3911 'VN' => __( 'Vietnam', 'pods' ),
3912 'WK' => __( 'Wake Island', 'pods' ),
3913 'WF' => __( 'Wallis and Futuna', 'pods' ),
3914 'EH' => __( 'Western Sahara', 'pods' ),
3915 'YE' => __( 'Yemen', 'pods' ),
3916 'ZM' => __( 'Zambia', 'pods' ),
3917 'ZW' => __( 'Zimbabwe', 'pods' ),
3918 'AX' => __( '�
3919 land Islands', 'pods' ),
3920 ];
3921
3922 return apply_filters( 'pods_form_ui_field_pick_data_countries', $data, $name, $value, $options, $pod, $id );
3923
3924 }
3925
3926 /**
3927 * Data callback for US States.
3928 *
3929 * @param string|null $name The name of the field.
3930 * @param string|array|null $value The value of the field.
3931 * @param array|null $options Field options.
3932 * @param array|null $pod Pod data.
3933 * @param int|null $id Item ID.
3934 *
3935 * @return array
3936 *
3937 * @since 2.3.0
3938 */
3939 public function data_us_states( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
3940
3941 $data = [
3942 'AL' => __( 'Alabama', 'pods' ),
3943 'AK' => __( 'Alaska', 'pods' ),
3944 'AZ' => __( 'Arizona', 'pods' ),
3945 'AR' => __( 'Arkansas', 'pods' ),
3946 'CA' => __( 'California', 'pods' ),
3947 'CO' => __( 'Colorado', 'pods' ),
3948 'CT' => __( 'Connecticut', 'pods' ),
3949 'DE' => __( 'Delaware', 'pods' ),
3950 'DC' => __( 'District Of Columbia', 'pods' ),
3951 'FL' => __( 'Florida', 'pods' ),
3952 'GA' => __( 'Georgia', 'pods' ),
3953 'HI' => __( 'Hawaii', 'pods' ),
3954 'ID' => __( 'Idaho', 'pods' ),
3955 'IL' => __( 'Illinois', 'pods' ),
3956 'IN' => __( 'Indiana', 'pods' ),
3957 'IA' => __( 'Iowa', 'pods' ),
3958 'KS' => __( 'Kansas', 'pods' ),
3959 'KY' => __( 'Kentucky', 'pods' ),
3960 'LA' => __( 'Louisiana', 'pods' ),
3961 'ME' => __( 'Maine', 'pods' ),
3962 'MD' => __( 'Maryland', 'pods' ),
3963 'MA' => __( 'Massachusetts', 'pods' ),
3964 'MI' => __( 'Michigan', 'pods' ),
3965 'MN' => __( 'Minnesota', 'pods' ),
3966 'MS' => __( 'Mississippi', 'pods' ),
3967 'MO' => __( 'Missouri', 'pods' ),
3968 'MT' => __( 'Montana', 'pods' ),
3969 'NE' => __( 'Nebraska', 'pods' ),
3970 'NV' => __( 'Nevada', 'pods' ),
3971 'NH' => __( 'New Hampshire', 'pods' ),
3972 'NJ' => __( 'New Jersey', 'pods' ),
3973 'NM' => __( 'New Mexico', 'pods' ),
3974 'NY' => __( 'New York', 'pods' ),
3975 'NC' => __( 'North Carolina', 'pods' ),
3976 'ND' => __( 'North Dakota', 'pods' ),
3977 'OH' => __( 'Ohio', 'pods' ),
3978 'OK' => __( 'Oklahoma', 'pods' ),
3979 'OR' => __( 'Oregon', 'pods' ),
3980 'PA' => __( 'Pennsylvania', 'pods' ),
3981 'RI' => __( 'Rhode Island', 'pods' ),
3982 'SC' => __( 'South Carolina', 'pods' ),
3983 'SD' => __( 'South Dakota', 'pods' ),
3984 'TN' => __( 'Tennessee', 'pods' ),
3985 'TX' => __( 'Texas', 'pods' ),
3986 'UT' => __( 'Utah', 'pods' ),
3987 'VT' => __( 'Vermont', 'pods' ),
3988 'VA' => __( 'Virginia', 'pods' ),
3989 'WA' => __( 'Washington', 'pods' ),
3990 'WV' => __( 'West Virginia', 'pods' ),
3991 'WI' => __( 'Wisconsin', 'pods' ),
3992 'WY' => __( 'Wyoming', 'pods' ),
3993 ];
3994
3995 return apply_filters( 'pods_form_ui_field_pick_data_us_states', $data, $name, $value, $options, $pod, $id );
3996
3997 }
3998
3999 /**
4000 * Data callback for CA Provinces.
4001 *
4002 * @param string|null $name The name of the field.
4003 * @param string|array|null $value The value of the field.
4004 * @param array|null $options Field options.
4005 * @param array|null $pod Pod data.
4006 * @param int|null $id Item ID.
4007 *
4008 * @return array
4009 *
4010 * @since 2.3.0
4011 */
4012 public function data_ca_provinces( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
4013
4014 $data = [
4015 'AB' => __( 'Alberta', 'pods' ),
4016 'BC' => __( 'British Columbia', 'pods' ),
4017 'MB' => __( 'Manitoba', 'pods' ),
4018 'NB' => __( 'New Brunswick', 'pods' ),
4019 'NL' => __( 'Newfoundland and Labrador', 'pods' ),
4020 'NT' => __( 'Northwest Territories', 'pods' ),
4021 'NS' => __( 'Nova Scotia', 'pods' ),
4022 'NU' => __( 'Nunavut', 'pods' ),
4023 'ON' => __( 'Ontario', 'pods' ),
4024 'PE' => __( 'Prince Edward Island', 'pods' ),
4025 'QC' => __( 'Quebec', 'pods' ),
4026 'SK' => __( 'Saskatchewan', 'pods' ),
4027 'YT' => __( 'Yukon', 'pods' ),
4028 ];
4029
4030 return apply_filters( 'pods_form_ui_field_pick_data_ca_provinces', $data, $name, $value, $options, $pod, $id );
4031
4032 }
4033
4034 /**
4035 * Data callback for Days of the Week.
4036 *
4037 * @param string|null $name The name of the field.
4038 * @param string|array|null $value The value of the field.
4039 * @param array|null $options Field options.
4040 * @param array|null $pod Pod data.
4041 * @param int|null $id Item ID.
4042 *
4043 * @return array
4044 *
4045 * @since 2.3.0
4046 */
4047 public function data_days_of_week( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
4048
4049 /**
4050 * @var WP_Locale
4051 */
4052 global $wp_locale;
4053
4054 return $wp_locale->weekday;
4055
4056 }
4057
4058 /**
4059 * Data callback for Months of the Year.
4060 *
4061 * @param string|null $name The name of the field.
4062 * @param string|array|null $value The value of the field.
4063 * @param array|null $options Field options.
4064 * @param array|null $pod Pod data.
4065 * @param int|null $id Item ID.
4066 *
4067 * @return array
4068 *
4069 * @since 2.3.0
4070 */
4071 public function data_months_of_year( $name = null, $value = null, $options = null, $pod = null, $id = null ) {
4072
4073 /**
4074 * @var WP_Locale
4075 */
4076 global $wp_locale;
4077
4078 return $wp_locale->month;
4079
4080 }
4081
4082 /**
4083 * Add our modal input to the form so we can track whether we're in our modal during saving or not.
4084 */
4085 public function admin_modal_input() {
4086
4087 if ( ! pods_is_modal_window() ) {
4088 return;
4089 }
4090
4091 echo '<input name="pods_modal" type="hidden" value="1" />';
4092
4093 }
4094
4095 /**
4096 * Bail to send new saved data back to our modal handler.
4097 *
4098 * @param int $item_id Item ID.
4099 * @param string $item_title Item title.
4100 * @param object $field_args Field arguments.
4101 */
4102 public function admin_modal_bail( $item_id, $item_title, $field_args ) {
4103
4104 $model_data = $this->build_dfv_field_item_data_recurse_item( $item_id, $item_title, $field_args );
4105 ?>
4106 <script type="text/javascript">
4107 window.parent.postMessage( {
4108 type: 'PODS_MESSAGE',
4109 data: <?php echo wp_json_encode( $model_data, JSON_HEX_TAG ); ?>,
4110 }, window.location.origin );
4111 </script>
4112 <?php
4113
4114 die();
4115
4116 }
4117
4118 /**
4119 * Bail to send new saved data back to our modal handler.
4120 *
4121 * @param int $item_id Item ID.
4122 * @param string $item_title Item title.
4123 * @param object $field_args Field arguments.
4124 */
4125 public function admin_modal_bail_JSON( $item_id, $item_title, $field_args ) {
4126
4127 $model_data = $this->build_dfv_field_item_data_recurse_item( $item_id, $item_title, $field_args );
4128 echo wp_json_encode( $model_data, JSON_HEX_TAG );
4129
4130 die();
4131 }
4132
4133 /**
4134 * Bail on Post save redirect for Admin modal.
4135 *
4136 * @param string $location The destination URL.
4137 * @param int $post_id The post ID.
4138 *
4139 * @return string
4140 */
4141 public function admin_modal_bail_post_redirect( $location, $post_id ) {
4142
4143 if ( ! pods_is_modal_window() ) {
4144 return $location;
4145 }
4146
4147 $post_title = get_the_title( $post_id );
4148
4149 $field_args = (object) [
4150 'options' => [
4151 'pick_object' => 'post_type',
4152 'pick_val' => get_post_type( $post_id ),
4153 ],
4154 'value' => [
4155 $post_id => $post_title,
4156 ],
4157 ];
4158
4159 $this->admin_modal_bail( $post_id, $post_title, $field_args );
4160
4161 return $location;
4162
4163 }
4164
4165 /**
4166 * Hook into term updating process to bail on redirect.
4167 */
4168 public function admin_modal_bail_term_action() {
4169
4170 if ( ! pods_is_modal_window() ) {
4171 return;
4172 }
4173
4174 if ( function_exists( 'get_current_screen' ) ) {
4175 $screen = get_current_screen();
4176
4177 if ( 'edit-tags' === $screen->base ) {
4178 // @todo Need more effort on the solution for add new handling.
4179 //add_action( 'admin_footer', [ $this, 'admin_modal_bail_term_action_add_new' ], 20 );
4180 }
4181 }
4182
4183 add_action( 'created_term', [ $this, 'admin_modal_bail_term' ], 10, 3 );
4184 add_action( 'edited_term', [ $this, 'admin_modal_bail_term' ], 10, 3 );
4185
4186 }
4187
4188 /**
4189 * Hook into term creation process to bail after success.
4190 *
4191 * @todo Try and catch the added tr node on the table tbody.
4192 */
4193 public function admin_modal_bail_term_action_add_new() {
4194 ?>
4195 <script type="text/javascript">
4196 jQuery( function( $ ) {
4197 /** @var {jQuery.Event} e */
4198 $( '.tags' ).on( 'DOMSubtreeModified', function( e ) {
4199 console.log( e );
4200
4201 if ( !==
4202 e.target.is( 'tbody#the-list' );
4203 )
4204 {
4205 return;
4206 }
4207
4208 const $theTermRow = $( e.target.innerHTML() );
4209 const titleRow = $theTermRow.find( '.column-name .row-title' );
4210 const actionView = $theTermRow.find( '.row-actions span.view a' );
4211
4212 const termData = {
4213 id: $theTermRow.find( '.check-column input' ).val(),
4214 icon: '',
4215 name: titleRow.text(),
4216 edit_link: titleRow.prop( 'href' ),
4217 link: actionView[ 0 ] ? actionView.prop( 'href' ) : '',
4218 selected: true,
4219 };
4220
4221 console.log( termData );
4222
4223 window.parent.postMessage( {
4224 type: 'PODS_MESSAGE',
4225 data: termData,
4226 }, window.location.origin );
4227 } );
4228 } );
4229 </script>
4230 <?php
4231 }
4232
4233 /**
4234 * Bail on Term save redirect for Admin modal.
4235 *
4236 * @param int $term_id Term ID.
4237 * @param int $tt_id Term taxonomy ID.
4238 * @param string $taxonomy Taxonomy slug.
4239 */
4240 public function admin_modal_bail_term( $term_id, $tt_id, $taxonomy ) {
4241
4242 if ( ! pods_is_modal_window() ) {
4243 return;
4244 }
4245
4246 $term = get_term( $term_id );
4247
4248 if ( ! $term || is_wp_error( $term ) ) {
4249 return;
4250 }
4251
4252 $field_args = (object) [
4253 'options' => [
4254 'pick_object' => 'taxonomy',
4255 'pick_val' => $term->taxonomy,
4256 ],
4257 'value' => [
4258 $term->term_id => $term->name,
4259 ],
4260 ];
4261
4262 $this->admin_modal_bail( $term->term_id, $term->name, $field_args );
4263
4264 }
4265
4266 /**
4267 * Hook into user updating process to bail on redirect.
4268 */
4269 public function admin_modal_bail_user_action() {
4270
4271 if ( ! pods_is_modal_window() ) {
4272 return;
4273 }
4274
4275 add_filter( 'wp_redirect', [ $this, 'admin_modal_bail_user_redirect' ] );
4276
4277 }
4278
4279 /**
4280 * Bail on User save redirect for Admin modal.
4281 *
4282 * @param string $location The destination URL.
4283 *
4284 * @return string
4285 */
4286 public function admin_modal_bail_user_redirect( $location ) {
4287
4288 if ( ! pods_is_modal_window() ) {
4289 return $location;
4290 }
4291
4292 global $user_id;
4293
4294 $user = get_userdata( $user_id );
4295
4296 if ( ! $user ) {
4297 return $location;
4298 }
4299
4300 $field_args = (object) [
4301 'options' => [
4302 'pick_object' => 'user',
4303 'pick_val' => '',
4304 ],
4305 'value' => [
4306 $user->ID => $user->display_name,
4307 ],
4308 ];
4309
4310 $this->admin_modal_bail( $user->ID, $user->display_name, $field_args );
4311
4312 return $location;
4313
4314 }
4315
4316 /**
4317 * Bail on Pod item save for Admin modal.
4318 *
4319 * @param int $id Item ID.
4320 * @param array $params save_pod_item parameters.
4321 * @param null|Pods $obj Pod object (if set).
4322 */
4323 public function admin_modal_bail_pod( $id, $params, $obj ) {
4324
4325 if ( ! pods_is_modal_window() ) {
4326 return;
4327 }
4328
4329 if ( ! $obj ) {
4330 $obj = pods_get_instance( $params['pod'] );
4331 }
4332
4333 if ( ! $obj || ! $obj->fetch( $id ) ) {
4334 return;
4335 }
4336
4337 $item_id = $obj->id();
4338 $item_title = $obj->index();
4339
4340 $field_args = (object) [
4341 'options' => [
4342 'pick_object' => $obj->pod_data['type'],
4343 'pick_val' => $obj->pod,
4344 ],
4345 'value' => [
4346 $obj->id() => $item_title,
4347 ],
4348 ];
4349
4350 $this->admin_modal_bail_JSON( $item_id, $item_title, $field_args );
4351
4352 }
4353
4354 /**
4355 * Build field data for Pods DFV.
4356 *
4357 * @param object $args {
4358 * Field information arguments.
4359 *
4360 * @type string $name Field name.
4361 * @type string $type Field type.
4362 * @type array $options Field options.
4363 * @type mixed $value Current value.
4364 * @type array $pod Pod information.
4365 * @type int|string $id Current item ID.
4366 * @type string $form_field_type HTML field type.
4367 * }
4368 *
4369 * @return array
4370 */
4371 public function build_dfv_field_data( $args ) {
4372 $data = parent::build_dfv_field_data( $args );
4373
4374 // Normalize arrays for multiple select.
4375 if ( is_array( $data['fieldValue'] ) ) {
4376 $data['fieldValue'] = array_values( $data['fieldValue'] );
4377 }
4378
4379 return $data;
4380 }
4381
4382 }
4383