PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.9.1
Secure Custom Fields v6.9.1
6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / includes / fields / class-acf-field-taxonomy.php
secure-custom-fields / includes / fields Last commit date
FlexibleContent 2 months ago class-acf-field-accordion.php 2 months ago class-acf-field-button-group.php 2 months ago class-acf-field-checkbox.php 2 days ago class-acf-field-clone.php 2 months ago class-acf-field-color_picker.php 2 months ago class-acf-field-date_picker.php 2 months ago class-acf-field-date_time_picker.php 2 months ago class-acf-field-email.php 2 months ago class-acf-field-file.php 2 months ago class-acf-field-flexible-content.php 1 week ago class-acf-field-gallery.php 3 weeks ago class-acf-field-google-map.php 2 months ago class-acf-field-group.php 2 months ago class-acf-field-icon_picker.php 7 months ago class-acf-field-image.php 2 months ago class-acf-field-link.php 2 months ago class-acf-field-message.php 1 year ago class-acf-field-nav-menu.php 1 week ago class-acf-field-number.php 2 months ago class-acf-field-oembed.php 3 weeks ago class-acf-field-output.php 1 year ago class-acf-field-page_link.php 3 weeks ago class-acf-field-password.php 2 months ago class-acf-field-post_object.php 3 weeks ago class-acf-field-radio.php 2 days ago class-acf-field-range.php 2 months ago class-acf-field-relationship.php 3 weeks ago class-acf-field-repeater.php 3 weeks ago class-acf-field-select.php 2 days ago class-acf-field-separator.php 1 year ago class-acf-field-tab.php 1 year ago class-acf-field-taxonomy.php 3 weeks ago class-acf-field-text.php 3 weeks ago class-acf-field-textarea.php 3 weeks ago class-acf-field-time_picker.php 2 months ago class-acf-field-true_false.php 2 months ago class-acf-field-url.php 3 weeks ago class-acf-field-user.php 3 weeks ago class-acf-field-wysiwyg.php 2 months ago class-acf-field.php 2 months ago class-acf-repeater-table.php 1 year ago index.php 1 year ago
class-acf-field-taxonomy.php
1068 lines
1 <?php
2
3 if ( ! class_exists( 'acf_field_taxonomy' ) ) :
4
5 class acf_field_taxonomy extends acf_field {
6
7
8 // vars
9 var $save_post_terms = array();
10
11
12 /**
13 * This function will setup the field type data
14 *
15 * @type function
16 * @date 5/03/2014
17 * @since ACF 5.0.0
18 */
19 public function initialize() {
20 $this->name = 'taxonomy';
21 $this->label = __( 'Taxonomy', 'secure-custom-fields' );
22 $this->category = 'relational';
23 $this->description = __( 'Allows the selection of one or more taxonomy terms based on the criteria and options specified in the fields settings.', 'secure-custom-fields' );
24 $this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-taxonomy.png';
25 $this->doc_url = 'https://developer.wordpress.org/secure-custom-fields/features/fields/taxonomy/';
26 $this->tutorial_url = 'https://developer.wordpress.org/secure-custom-fields/features/fields/taxonomy/taxonomy-tutorial/';
27 $this->defaults = array(
28 'taxonomy' => 'category',
29 'field_type' => 'checkbox',
30 'multiple' => 0,
31 'allow_null' => 0,
32 'return_format' => 'id',
33 'add_term' => 1, // 5.2.3
34 'load_terms' => 0, // 5.2.7
35 'save_terms' => 0, // 5.2.7
36 'bidirectional_target' => array(),
37 );
38
39 // Register filter variations.
40 acf_add_filter_variations( 'acf/fields/taxonomy/query', array( 'name', 'key' ), 1 );
41 acf_add_filter_variations( 'acf/fields/taxonomy/result', array( 'name', 'key' ), 2 );
42
43 // ajax
44 add_action( 'wp_ajax_acf/fields/taxonomy/query', array( $this, 'ajax_query' ) );
45 add_action( 'wp_ajax_nopriv_acf/fields/taxonomy/query', array( $this, 'ajax_query' ) );
46 add_action( 'wp_ajax_acf/fields/taxonomy/add_term', array( $this, 'ajax_add_term' ) );
47 add_filter( 'acf/conditional_logic/choices', array( $this, 'render_field_taxonomy_conditional_choices' ), 10, 3 );
48
49 // actions
50 add_action( 'acf/save_post', array( $this, 'save_post' ), 15, 1 );
51 }
52
53 /**
54 * Returns AJAX results for the Taxonomy field.
55 *
56 * @since ACF 5.0.0
57 *
58 * @return void
59 */
60 public function ajax_query() {
61 $nonce = acf_request_arg( 'nonce', '' );
62 $key = acf_request_arg( 'field_key', '' );
63 $conditional_logic = (bool) acf_request_arg( 'conditional_logic', false );
64
65 if ( $conditional_logic ) {
66 if ( ! acf_current_user_can_admin() ) {
67 die();
68 }
69
70 // Use the standard ACF admin nonce.
71 $nonce = '';
72 $key = '';
73 }
74
75 if ( ! acf_verify_ajax( $nonce, $key, ! $conditional_logic, 'taxonomy' ) ) {
76 die();
77 }
78
79 acf_send_ajax_results( $this->get_ajax_query( $_POST ) );
80 }
81
82 /**
83 * This function will return an array of data formatted for use in a select2 AJAX response
84 *
85 * @type function
86 * @date 15/10/2014
87 * @since ACF 5.0.9
88 *
89 * @param $options (array)
90 * @return (array)
91 */
92 function get_ajax_query( $options = array() ) {
93 $options = acf_parse_args(
94 $options,
95 array(
96 'post_id' => 0,
97 's' => '',
98 'field_key' => '',
99 'term_id' => '',
100 'include' => '',
101 'paged' => 1,
102 )
103 );
104
105 $field = acf_get_field( $options['field_key'] );
106 if ( ! $field ) {
107 return false;
108 }
109
110 // if options include isset, then we are loading a specific term.
111 if ( ! empty( $options['include'] ) ) {
112 $options['term_id'] = $options['include'];
113 // paged should be 1.
114 $options['paged'] = 1;
115 }
116
117 // Bail early if taxonomy does not exist.
118 if ( ! taxonomy_exists( $field['taxonomy'] ) ) {
119 return false;
120 }
121
122 $results = array();
123 $is_hierarchical = is_taxonomy_hierarchical( $field['taxonomy'] );
124 $is_pagination = ( $options['paged'] > 0 );
125 $is_search = false;
126 $limit = 20;
127 $offset = 20 * ( $options['paged'] - 1 );
128
129 $args = array(
130 'taxonomy' => $field['taxonomy'],
131 'hide_empty' => false,
132 );
133
134 // Don't bother for hierarchical terms, we will need to load all terms anyway.
135 if ( $is_pagination && ! $is_hierarchical ) {
136 $args['number'] = $limit;
137 $args['offset'] = $offset;
138 }
139
140 // search
141 if ( $options['s'] !== '' ) {
142
143 // strip slashes (search may be integer)
144 $s = wp_unslash( strval( $options['s'] ) );
145
146 $args['search'] = isset( $options['term_id'] ) && $options['term_id'] ? '' : $s;
147 $is_search = true;
148 }
149
150 $args = apply_filters( 'acf/fields/taxonomy/query', $args, $field, $options['post_id'] );
151
152 if ( ! empty( $options['include'] ) ) {
153 // Limit search to a specific id if one is provided.
154 $args['include'] = $options['include'];
155 }
156
157 $terms = acf_get_terms( $args );
158
159 // Sort hierarchical.
160 if ( $is_hierarchical ) {
161 $limit = acf_maybe_get( $args, 'number', $limit );
162 $offset = acf_maybe_get( $args, 'offset', $offset );
163
164 $parent = acf_maybe_get( $args, 'parent', 0 );
165 $parent = acf_maybe_get( $args, 'child_of', $parent );
166
167 // This will fail if a search has taken place because parents wont exist.
168 if ( ! $is_search ) {
169 $ordered_terms = _get_term_children( $parent, $terms, $field['taxonomy'] );
170 // Check for empty array. Possible if parent did not exist within original data.
171 if ( ! empty( $ordered_terms ) ) {
172 $terms = $ordered_terms;
173 }
174 }
175
176 // Fake pagination.
177 if ( $is_pagination && ! $options['include'] ) {
178 $terms = array_slice( $terms, $offset, $limit );
179 }
180 }
181
182 // Append to r.
183 foreach ( $terms as $term ) {
184
185 // Add to json.
186 $results[] = array(
187 'id' => $term->term_id,
188 'text' => $this->get_term_title( $term, $field, $options['post_id'], true ),
189 );
190 }
191
192 $response = array(
193 'results' => $results,
194 'limit' => $limit,
195 );
196
197 return $response;
198 }
199
200 /**
201 * Returns the Term's title displayed in the field UI.
202 *
203 * @since ACF 5.0.0
204 *
205 * @param WP_Term $term The term object.
206 * @param array $field The field settings.
207 * @param mixed $post_id The post_id being edited.
208 * @param boolean $unescape Should we return an unescaped post title.
209 * @return string
210 */
211 function get_term_title( $term, $field, $post_id = 0, $unescape = false ) {
212 $title = acf_get_term_title( $term );
213
214 // Default $post_id to current post being edited.
215 $post_id = $post_id ? $post_id : acf_get_form_data( 'post_id' );
216
217 // unescape for select2 output which handles the escaping.
218 if ( $unescape ) {
219 $title = html_entity_decode( $title );
220 }
221
222 /**
223 * Filters the term title.
224 *
225 * @date 1/11/2013
226 * @since ACF 5.0.0
227 *
228 * @param string $title The term title.
229 * @param WP_Term $term The term object.
230 * @param array $field The field settings.
231 * @param (int|string) $post_id The post_id being edited.
232 */
233 return apply_filters( 'acf/fields/taxonomy/result', $title, $term, $field, $post_id );
234 }
235
236
237 /**
238 * This function will return an array of terms for a given field value
239 *
240 * @type function
241 * @date 13/06/2014
242 * @since ACF 5.0.0
243 *
244 * @param $value (array)
245 * @return $value
246 */
247 function get_terms( $value, $taxonomy = 'category' ) {
248
249 // load terms in 1 query to save multiple DB calls from following code
250 if ( count( $value ) > 1 ) {
251 $terms = acf_get_terms(
252 array(
253 'taxonomy' => $taxonomy,
254 'include' => $value,
255 'hide_empty' => false,
256 )
257 );
258 }
259
260 // update value to include $post
261 foreach ( array_keys( $value ) as $i ) {
262 $value[ $i ] = get_term( $value[ $i ], $taxonomy );
263 }
264
265 // filter out null values
266 $value = array_filter( $value );
267
268 // return
269 return $value;
270 }
271
272
273 /**
274 * This filter is applied to the $value after it is loaded from the db
275 *
276 * @type filter
277 * @since ACF 3.6
278 * @date 23/01/13
279 *
280 * @param $value - the value found in the database
281 * @param $post_id - the post_id from which the value was loaded from
282 * @param $field - the field array holding all the field options
283 *
284 * @return $value - the value to be saved in te database
285 */
286 function load_value( $value, $post_id, $field ) {
287
288 // get valid terms
289 $value = acf_get_valid_terms( $value, $field['taxonomy'] );
290
291 // load_terms
292 if ( $field['load_terms'] ) {
293
294 // Decode $post_id for $type and $id.
295 $decoded = acf_decode_post_id( $post_id );
296 $type = $decoded['type'];
297 $id = $decoded['id'];
298
299 if ( $type === 'block' ) {
300 // Get parent block...
301 }
302
303 // get terms
304 $term_ids = wp_get_object_terms(
305 $id,
306 $field['taxonomy'],
307 array(
308 'fields' => 'ids',
309 'orderby' => 'none',
310 )
311 );
312
313 // bail early if no terms
314 if ( empty( $term_ids ) || is_wp_error( $term_ids ) ) {
315 return false;
316 }
317
318 // sort
319 if ( ! empty( $value ) ) {
320 $order = array();
321
322 foreach ( $term_ids as $i => $v ) {
323 $order[ $i ] = array_search( $v, $value );
324 }
325
326 array_multisort( $order, $term_ids );
327 }
328
329 // update value
330 $value = $term_ids;
331 }
332
333 // convert back from array if necessary
334 if ( $field['field_type'] == 'select' || $field['field_type'] == 'radio' ) {
335 $value = array_shift( $value );
336 }
337
338 // return
339 return $value;
340 }
341
342
343 /**
344 * Filters the field value before it is saved into the database.
345 *
346 * @since ACF 3.6
347 *
348 * @param mixed $value The value which will be saved in the database.
349 * @param integer $post_id The post_id of which the value will be saved.
350 * @param array $field The field array holding all the field options.
351 * @return mixed $value The modified value.
352 */
353 public function update_value( $value, $post_id, $field ) {
354
355 if ( is_array( $value ) ) {
356 $value = array_filter( $value );
357 }
358
359 acf_update_bidirectional_values( acf_get_array( $value ), $post_id, $field, 'term' );
360
361 // save_terms if enabled.
362 if ( $field['save_terms'] ) {
363
364 // vars
365 $taxonomy = $field['taxonomy'];
366
367 // force value to array.
368 $term_ids = acf_get_array( $value );
369
370 // convert to int.
371 $term_ids = array_map( 'intval', $term_ids );
372
373 // get existing term id's (from a previously saved field).
374 $old_term_ids = isset( $this->save_post_terms[ $taxonomy ] ) ? $this->save_post_terms[ $taxonomy ] : array();
375
376 // append
377 $this->save_post_terms[ $taxonomy ] = array_merge( $old_term_ids, $term_ids );
378
379 // if called directly from frontend update_field().
380 if ( ! did_action( 'acf/save_post' ) ) {
381 $this->save_post( $post_id );
382 return $value;
383 }
384 }
385
386 return $value;
387 }
388
389 /**
390 * This function will save any terms in the save_post_terms array
391 *
392 * @since ACF 5.0.9
393 *
394 * @param mixed $post_id The ACF post ID to save to.
395 * @return void
396 */
397 public function save_post( $post_id ) {
398 // Check for saved terms.
399 if ( ! empty( $this->save_post_terms ) ) {
400 /**
401 * Determine object ID allowing for non "post" $post_id (user, taxonomy, etc).
402 * Although not fully supported by WordPress, non "post" objects may use the term relationships table.
403 * Sharing taxonomies across object types is discouraged, but unique taxonomies work well.
404 * Note: Do not attempt to restrict to "post" only. This has been attempted in 5.8.9 and later reverted.
405 */
406 $decoded = acf_decode_post_id( $post_id );
407 $type = $decoded['type'];
408 $id = $decoded['id'];
409
410 if ( $type === 'block' ) {
411 // Get parent block...
412 }
413
414 // Loop over taxonomies and save terms.
415 foreach ( $this->save_post_terms as $taxonomy => $term_ids ) {
416 wp_set_object_terms( $id, $term_ids, $taxonomy, false );
417 }
418
419 // Reset storage.
420 $this->save_post_terms = array();
421 }
422 }
423
424 /**
425 * This filter is applied to the $value after it is loaded from the db and before it is returned to the template
426 *
427 * @type filter
428 * @since ACF 3.6
429 * @date 23/01/13
430 *
431 * @param $value (mixed) the value which was loaded from the database
432 * @param $post_id (mixed) the post_id from which the value was loaded
433 * @param $field (array) the field array holding all the field options
434 *
435 * @return $value (mixed) the modified value
436 */
437 function format_value( $value, $post_id, $field ) {
438
439 // bail early if no value
440 if ( empty( $value ) ) {
441 return false;
442 }
443
444 // force value to array
445 $value = acf_get_array( $value );
446
447 // load posts if needed
448 if ( $field['return_format'] == 'object' ) {
449
450 // get posts
451 $value = $this->get_terms( $value, $field['taxonomy'] );
452 }
453
454 // convert back from array if necessary
455 if ( $field['field_type'] == 'select' || $field['field_type'] == 'radio' ) {
456 $value = array_shift( $value );
457 }
458
459 // return
460 return $value;
461 }
462
463 /**
464 * Renders the Taxonomy field.
465 *
466 * @since ACF 3.6
467 *
468 * @param array $field The field settings array.
469 * @return void
470 */
471 public function render_field( $field ) {
472 // force value to array
473 $field['value'] = acf_get_array( $field['value'] );
474
475 $nonce = wp_create_nonce( 'acf_field_' . $this->name . '_' . $field['key'] );
476
477 // vars
478 $div = array(
479 'class' => 'acf-taxonomy-field',
480 'data-save' => $field['save_terms'],
481 'data-ftype' => $field['field_type'],
482 'data-taxonomy' => $field['taxonomy'],
483 'data-allow_null' => $field['allow_null'],
484 'data-nonce' => $nonce,
485 );
486 // get taxonomy
487 $taxonomy = get_taxonomy( $field['taxonomy'] );
488
489 // bail early if taxonomy does not exist
490 if ( ! $taxonomy ) {
491 return;
492 }
493
494 ?>
495 <div <?php echo acf_esc_attrs( $div ); ?>>
496 <?php if ( $field['add_term'] && current_user_can( $taxonomy->cap->manage_terms ) ) : ?>
497 <div class="acf-actions -hover">
498 <a href="#" class="acf-icon -plus acf-js-tooltip small" data-name="add" title="<?php echo esc_attr( $taxonomy->labels->add_new_item ); ?>"></a>
499 </div>
500 <?php
501 endif;
502
503 if ( $field['field_type'] == 'select' ) {
504 $field['multiple'] = 0;
505
506 $this->render_field_select( $field, $nonce );
507 } elseif ( $field['field_type'] == 'multi_select' ) {
508 $field['multiple'] = 1;
509
510 $this->render_field_select( $field, $nonce );
511 } elseif ( $field['field_type'] == 'radio' ) {
512 $this->render_field_checkbox( $field );
513 } elseif ( $field['field_type'] == 'checkbox' ) {
514 $this->render_field_checkbox( $field );
515 }
516
517 ?>
518 </div>
519 <?php
520 }
521
522 /**
523 * Create the HTML interface for your field
524 *
525 * @type action
526 * @since ACF 3.6
527 * @date 23/01/13
528 *
529 * @param $field - an array holding all the field's data
530 */
531 function render_field_select( $field, $nonce ) {
532
533 // Change Field into a select
534 $field['type'] = 'select';
535 $field['ui'] = 1;
536 $field['ajax'] = 1;
537 $field['nonce'] = $nonce;
538 $field['choices'] = array();
539
540 // value
541 if ( ! empty( $field['value'] ) ) {
542
543 // get terms
544 $terms = $this->get_terms( $field['value'], $field['taxonomy'] );
545
546 // set choices
547 if ( ! empty( $terms ) ) {
548 foreach ( array_keys( $terms ) as $i ) {
549
550 // vars
551 $term = acf_extract_var( $terms, $i );
552
553 // append to choices
554 $field['choices'][ $term->term_id ] = $this->get_term_title( $term, $field );
555 }
556 }
557 }
558
559 // render select
560 acf_render_field( $field );
561 }
562
563
564 /**
565 * Create the HTML interface for your field
566 *
567 * @since ACF 3.6
568 *
569 * @param array $field an array holding all the field's data.
570 */
571 public function render_field_checkbox( $field ) {
572
573 // hidden input.
574 acf_hidden_input(
575 array(
576 'type' => 'hidden',
577 'name' => $field['name'],
578 )
579 );
580
581 // checkbox saves an array.
582 if ( 'checkbox' === $field['field_type'] ) {
583 $field['name'] .= '[]';
584 }
585
586 // taxonomy.
587 $taxonomy_obj = get_taxonomy( $field['taxonomy'] );
588
589 // include walker.
590 acf_include( 'includes/walkers/class-acf-walker-taxonomy-field.php' );
591
592 // vars.
593 $args = array(
594 'taxonomy' => $field['taxonomy'],
595 /* translators: %s: Taxonomy name */
596 'show_option_none' => sprintf( _x( 'No %s', 'No Terms', 'secure-custom-fields' ), $taxonomy_obj->labels->name ),
597 'hide_empty' => false,
598 'style' => 'none',
599 'walker' => new ACF_Taxonomy_Field_Walker( $field ),
600 );
601
602 // filter for 3rd party customization.
603 $args = apply_filters( 'acf/fields/taxonomy/wp_list_categories', $args, $field );
604 $args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/name=' . $field['_name'], $args, $field );
605 $args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/key=' . $field['key'], $args, $field );
606
607 // Build UL attributes for accessibility and consistency.
608 $ul = array(
609 'class' => 'acf-checkbox-list acf-bl',
610 'role' => 'radio' === $field['field_type'] ? 'radiogroup' : 'group',
611 );
612
613 if ( ! empty( $field['id'] ) ) {
614 $ul['aria-labelledby'] = $field['id'] . '-label';
615 }
616 ?>
617 <div class="categorychecklist-holder">
618 <ul <?php echo acf_esc_attrs( $ul ); ?>>
619 <?php wp_list_categories( $args ); ?>
620 </ul>
621 </div>
622 <?php
623 }
624
625
626 /**
627 * Create extra options for your field. This is rendered when editing a field.
628 * The value of $field['name'] can be used (like bellow) to save extra data to the $field
629 *
630 * @type action
631 * @since ACF 3.6
632 * @date 23/01/13
633 *
634 * @param $field - an array holding all the field's data
635 */
636 function render_field_settings( $field ) {
637 acf_render_field_setting(
638 $field,
639 array(
640 'label' => __( 'Taxonomy', 'secure-custom-fields' ),
641 'instructions' => __( 'Select the taxonomy to be displayed', 'secure-custom-fields' ),
642 'type' => 'select',
643 'name' => 'taxonomy',
644 'choices' => acf_get_taxonomy_labels(),
645 )
646 );
647
648 acf_render_field_setting(
649 $field,
650 array(
651 'label' => __( 'Create Terms', 'secure-custom-fields' ),
652 'instructions' => __( 'Allow new terms to be created whilst editing', 'secure-custom-fields' ),
653 'name' => 'add_term',
654 'type' => 'true_false',
655 'ui' => 1,
656 )
657 );
658
659 acf_render_field_setting(
660 $field,
661 array(
662 'label' => __( 'Save Terms', 'secure-custom-fields' ),
663 'instructions' => __( 'Connect selected terms to the post', 'secure-custom-fields' ),
664 'name' => 'save_terms',
665 'type' => 'true_false',
666 'ui' => 1,
667 )
668 );
669
670 acf_render_field_setting(
671 $field,
672 array(
673 'label' => __( 'Load Terms', 'secure-custom-fields' ),
674 'instructions' => __( 'Load value from posts terms', 'secure-custom-fields' ),
675 'name' => 'load_terms',
676 'type' => 'true_false',
677 'ui' => 1,
678 )
679 );
680
681 acf_render_field_setting(
682 $field,
683 array(
684 'label' => __( 'Return Value', 'secure-custom-fields' ),
685 'instructions' => '',
686 'type' => 'radio',
687 'name' => 'return_format',
688 'choices' => array(
689 'object' => __( 'Term Object', 'secure-custom-fields' ),
690 'id' => __( 'Term ID', 'secure-custom-fields' ),
691 ),
692 'layout' => 'horizontal',
693 )
694 );
695
696 acf_render_field_setting(
697 $field,
698 array(
699 'label' => __( 'Appearance', 'secure-custom-fields' ),
700 'instructions' => __( 'Select the appearance of this field', 'secure-custom-fields' ),
701 'type' => 'select',
702 'name' => 'field_type',
703 'optgroup' => true,
704 'choices' => array(
705 __( 'Multiple Values', 'secure-custom-fields' ) => array(
706 'checkbox' => __( 'Checkbox', 'secure-custom-fields' ),
707 'multi_select' => __( 'Multi Select', 'secure-custom-fields' ),
708 ),
709 __( 'Single Value', 'secure-custom-fields' ) => array(
710 'radio' => __( 'Radio Buttons', 'secure-custom-fields' ),
711 'select' => _x( 'Select', 'noun', 'secure-custom-fields' ),
712 ),
713 ),
714 )
715 );
716
717 acf_render_field_setting(
718 $field,
719 array(
720 'label' => __( 'Allow Null', 'secure-custom-fields' ),
721 'instructions' => '',
722 'name' => 'allow_null',
723 'type' => 'true_false',
724 'ui' => 1,
725 'conditions' => array(
726 'field' => 'field_type',
727 'operator' => '!=',
728 'value' => 'checkbox',
729 ),
730 )
731 );
732 }
733
734 /**
735 * Renders the field settings used in the "Advanced" tab.
736 *
737 * @since ACF 6.2
738 *
739 * @param array $field The field settings array.
740 * @return void
741 */
742 public function render_field_advanced_settings( $field ) {
743 acf_render_bidirectional_field_settings( $field );
744 }
745
746 /**
747 * Filters choices in taxonomy conditions.
748 *
749 * @since ACF 6.3
750 *
751 * @param array $choices The selected choice.
752 * @param array $conditional_field The conditional field settings object.
753 * @param string $rule_value The rule value.
754 * @return mixed
755 */
756 public function render_field_taxonomy_conditional_choices( $choices, $conditional_field, $rule_value ) {
757 if ( is_array( $conditional_field ) && $conditional_field['type'] === 'taxonomy' ) {
758 if ( ! empty( $rule_value ) ) {
759 $term = get_term( $rule_value );
760 $choices = array( $rule_value => $term->name );
761 }
762 }
763 return $choices;
764 }
765
766
767 /**
768 * AJAX handler for adding Taxonomy field terms.
769 *
770 * @since ACF 5.2.3
771 *
772 * @return void
773 */
774 public function ajax_add_term() {
775 $args = acf_request_args(
776 array(
777 'nonce' => '',
778 'field_key' => '',
779 'term_name' => '',
780 'term_parent' => '',
781 )
782 );
783
784 if ( ! acf_verify_ajax( $args['nonce'], $args['field_key'], true, 'taxonomy' ) ) {
785 die();
786 }
787
788 // load field
789 $field = acf_get_field( $args['field_key'] );
790 if ( ! $field ) {
791 die();
792 }
793
794 // vars
795 $taxonomy_obj = get_taxonomy( $field['taxonomy'] );
796 $taxonomy_label = $taxonomy_obj->labels->singular_name;
797
798 // validate cap
799 // note: this situation should never occur due to condition of the add new button
800 if ( ! current_user_can( $taxonomy_obj->cap->manage_terms ) ) {
801 wp_send_json_error(
802 array(
803 /* translators: %s: Taxonomy name */
804 'error' => sprintf( __( 'User unable to add new %s', 'secure-custom-fields' ), $taxonomy_label ),
805 )
806 );
807 }
808
809 // save?
810 if ( $args['term_name'] ) {
811
812 // exists
813 if ( term_exists( $args['term_name'], $field['taxonomy'], $args['term_parent'] ) ) {
814 wp_send_json_error(
815 array(
816 /* translators: %s: Taxonomy name */
817 'error' => sprintf( __( '%s already exists', 'secure-custom-fields' ), $taxonomy_label ),
818 )
819 );
820 }
821
822 // vars
823 $extra = array();
824 if ( $args['term_parent'] ) {
825 $extra['parent'] = (int) $args['term_parent'];
826 }
827
828 // insert
829 $data = wp_insert_term( $args['term_name'], $field['taxonomy'], $extra );
830
831 // error
832 if ( is_wp_error( $data ) ) {
833 wp_send_json_error(
834 array(
835 'error' => $data->get_error_message(),
836 )
837 );
838 }
839
840 // load term
841 $term = get_term( $data['term_id'] );
842
843 // prepend ancestors count to term name
844 $prefix = '';
845 $ancestors = get_ancestors( $term->term_id, $term->taxonomy );
846 if ( ! empty( $ancestors ) ) {
847 $prefix = str_repeat( '- ', count( $ancestors ) );
848 }
849
850 // success
851 wp_send_json_success(
852 array(
853 /* translators: %s: Taxonomy name */
854 'message' => sprintf( __( '%s added', 'secure-custom-fields' ), $taxonomy_label ),
855 'term_id' => $term->term_id,
856 'term_name' => $term->name,
857 'term_label' => $prefix . $term->name,
858 'term_parent' => $term->parent,
859 )
860 );
861 }
862
863 ?>
864 <form method="post">
865 <?php
866
867 acf_render_field_wrap(
868 array(
869 'label' => __( 'Name', 'secure-custom-fields' ),
870 'name' => 'term_name',
871 'type' => 'text',
872 )
873 );
874
875 if ( is_taxonomy_hierarchical( $field['taxonomy'] ) ) {
876 $choices = array();
877 $response = $this->get_ajax_query( $args );
878
879 if ( $response ) {
880 foreach ( $response['results'] as $v ) {
881 $choices[ $v['id'] ] = $v['text'];
882 }
883 }
884
885 acf_render_field_wrap(
886 array(
887 'label' => __( 'Parent', 'secure-custom-fields' ),
888 'name' => 'term_parent',
889 'type' => 'select',
890 'allow_null' => 1,
891 'ui' => 0,
892 'choices' => $choices,
893 )
894 );
895 }
896
897 ?>
898 <p class="acf-submit">
899 <button class="acf-submit-button button button-primary" type="submit"><?php esc_html_e( 'Add', 'secure-custom-fields' ); ?></button>
900 </p>
901 </form><?php
902
903 // die
904 die;
905 }
906
907 /**
908 * Return the schema array for the REST API.
909 *
910 * @param array $field
911 * @return array
912 */
913 public function get_rest_schema( array $field ) {
914 $schema = array(
915 'type' => array( 'integer', 'array', 'null' ),
916 'required' => ! empty( $field['required'] ),
917 'items' => array(
918 'type' => 'integer',
919 ),
920 );
921
922 if ( empty( $field['allow_null'] ) ) {
923 $schema['minItems'] = 1;
924 }
925
926 if ( in_array( $field['field_type'], array( 'radio', 'select' ) ) ) {
927 $schema['maxItems'] = 1;
928 }
929
930 return $schema;
931 }
932
933 /**
934 * @see \acf_field::get_rest_links()
935 * @param mixed $value The raw (unformatted) field value.
936 * @param integer|string $post_id
937 * @param array $field
938 * @return array
939 */
940 public function get_rest_links( $value, $post_id, array $field ) {
941 $links = array();
942
943 if ( empty( $value ) ) {
944 return $links;
945 }
946
947 foreach ( (array) $value as $object_id ) {
948 $term = get_term( $object_id );
949 if ( ! $term instanceof WP_Term ) {
950 continue;
951 }
952
953 $rest_base = acf_get_object_type_rest_base( get_taxonomy( $term->taxonomy ) );
954 if ( ! $rest_base ) {
955 continue;
956 }
957
958 $links[] = array(
959 'rel' => 'acf:term',
960 'href' => rest_url( sprintf( '/wp/v2/%s/%s', $rest_base, $object_id ) ),
961 'embeddable' => true,
962 'taxonomy' => $term->taxonomy,
963 );
964 }
965
966 return $links;
967 }
968
969 /**
970 * Returns an array of JSON-LD Property output types that are supported by this field type.
971 *
972 * @since 6.8
973 *
974 * @return string[]
975 */
976 public function get_jsonld_output_types(): array {
977 return array( 'DefinedTerm', 'Text' );
978 }
979
980 /**
981 * Formats the field value for JSON-LD output.
982 *
983 * @since 6.8.0
984 *
985 * @param mixed $value The value of the field.
986 * @param integer|string $post_id The ID of the post.
987 * @param array $field The field array.
988 * @return mixed
989 */
990 public function format_value_for_jsonld( $value, $post_id, $field ) {
991 if ( empty( $value ) ) {
992 return null;
993 }
994
995 // Get output format with fallback.
996 $output_format = $field['schema_output_format'] ?? '';
997 if ( empty( $output_format ) ) {
998 $property = $field['schema_property'] ?? '';
999 $output_format = \SCF\AI\GEO\Schema::get_default_output_format( $this->name, $property );
1000 }
1001
1002 // Default to Text if no format determined.
1003 if ( empty( $output_format ) ) {
1004 $output_format = 'Text';
1005 }
1006
1007 // Force value to array for consistent processing.
1008 $value = acf_get_array( $value );
1009
1010 // Get term objects.
1011 $terms = $this->get_terms( $value, $field['taxonomy'] );
1012
1013 if ( empty( $terms ) ) {
1014 return null;
1015 }
1016
1017 // Format based on output format.
1018 $formatted = array();
1019 foreach ( $terms as $term ) {
1020 if ( ! $term instanceof \WP_Term ) {
1021 continue;
1022 }
1023
1024 if ( 'Text' === $output_format ) {
1025 $formatted[] = $term->name;
1026 } else {
1027 // DefinedTerm format.
1028 $term_data = array(
1029 '@type' => 'DefinedTerm',
1030 'name' => $term->name,
1031 );
1032
1033 // Add term URL if available.
1034 $term_link = get_term_link( $term );
1035 if ( ! is_wp_error( $term_link ) ) {
1036 $term_data['url'] = $term_link;
1037 }
1038
1039 // Add term ID as identifier.
1040 $term_data['identifier'] = (string) $term->term_id;
1041
1042 $formatted[] = $term_data;
1043 }
1044 }
1045
1046 if ( empty( $formatted ) ) {
1047 return null;
1048 }
1049
1050 // Return single value for single-value field types.
1051 // Radio is always single. Select is single unless multiple is enabled.
1052 $is_single = 'radio' === $field['field_type'] ||
1053 ( 'select' === $field['field_type'] && empty( $field['multiple'] ) );
1054
1055 if ( $is_single ) {
1056 return $formatted[0];
1057 }
1058
1059 return $formatted;
1060 }
1061 }
1062
1063
1064 // initialize
1065 acf_register_field_type( 'acf_field_taxonomy' );
1066 endif; // class_exists check
1067
1068 ?>