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