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