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