class-acf-field-taxonomy.php 22 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  1. <?php
  2. if ( ! class_exists( 'acf_field_taxonomy' ) ) :
  3. class acf_field_taxonomy extends acf_field {
  4. // vars
  5. var $save_post_terms = array();
  6. /*
  7. * __construct
  8. *
  9. * This function will setup the field type data
  10. *
  11. * @type function
  12. * @date 5/03/2014
  13. * @since 5.0.0
  14. *
  15. * @param n/a
  16. * @return n/a
  17. */
  18. function initialize() {
  19. // vars
  20. $this->name = 'taxonomy';
  21. $this->label = __( 'Taxonomy', 'acf' );
  22. $this->category = 'relational';
  23. $this->defaults = array(
  24. 'taxonomy' => 'category',
  25. 'field_type' => 'checkbox',
  26. 'multiple' => 0,
  27. 'allow_null' => 0,
  28. 'return_format' => 'id',
  29. 'add_term' => 1, // 5.2.3
  30. 'load_terms' => 0, // 5.2.7
  31. 'save_terms' => 0, // 5.2.7
  32. );
  33. // Register filter variations.
  34. acf_add_filter_variations( 'acf/fields/taxonomy/query', array( 'name', 'key' ), 1 );
  35. acf_add_filter_variations( 'acf/fields/taxonomy/result', array( 'name', 'key' ), 2 );
  36. // ajax
  37. add_action( 'wp_ajax_acf/fields/taxonomy/query', array( $this, 'ajax_query' ) );
  38. add_action( 'wp_ajax_nopriv_acf/fields/taxonomy/query', array( $this, 'ajax_query' ) );
  39. add_action( 'wp_ajax_acf/fields/taxonomy/add_term', array( $this, 'ajax_add_term' ) );
  40. // actions
  41. add_action( 'acf/save_post', array( $this, 'save_post' ), 15, 1 );
  42. }
  43. /*
  44. * ajax_query
  45. *
  46. * description
  47. *
  48. * @type function
  49. * @date 24/10/13
  50. * @since 5.0.0
  51. *
  52. * @param $post_id (int)
  53. * @return $post_id (int)
  54. */
  55. function ajax_query() {
  56. // validate
  57. if ( ! acf_verify_ajax() ) {
  58. die();
  59. }
  60. // get choices
  61. $response = $this->get_ajax_query( $_POST );
  62. // return
  63. acf_send_ajax_results( $response );
  64. }
  65. /*
  66. * get_ajax_query
  67. *
  68. * This function will return an array of data formatted for use in a select2 AJAX response
  69. *
  70. * @type function
  71. * @date 15/10/2014
  72. * @since 5.0.9
  73. *
  74. * @param $options (array)
  75. * @return (array)
  76. */
  77. function get_ajax_query( $options = array() ) {
  78. // defaults
  79. $options = acf_parse_args(
  80. $options,
  81. array(
  82. 'post_id' => 0,
  83. 's' => '',
  84. 'field_key' => '',
  85. 'paged' => 0,
  86. )
  87. );
  88. // load field
  89. $field = acf_get_field( $options['field_key'] );
  90. if ( ! $field ) {
  91. return false;
  92. }
  93. // bail early if taxonomy does not exist
  94. if ( ! taxonomy_exists( $field['taxonomy'] ) ) {
  95. return false;
  96. }
  97. // vars
  98. $results = array();
  99. $is_hierarchical = is_taxonomy_hierarchical( $field['taxonomy'] );
  100. $is_pagination = ( $options['paged'] > 0 );
  101. $is_search = false;
  102. $limit = 20;
  103. $offset = 20 * ( $options['paged'] - 1 );
  104. // args
  105. $args = array(
  106. 'taxonomy' => $field['taxonomy'],
  107. 'hide_empty' => false,
  108. );
  109. // pagination
  110. // - don't bother for hierarchial terms, we will need to load all terms anyway
  111. if ( $is_pagination && ! $is_hierarchical ) {
  112. $args['number'] = $limit;
  113. $args['offset'] = $offset;
  114. }
  115. // search
  116. if ( $options['s'] !== '' ) {
  117. // strip slashes (search may be integer)
  118. $s = wp_unslash( strval( $options['s'] ) );
  119. // update vars
  120. $args['search'] = $s;
  121. $is_search = true;
  122. }
  123. // filters
  124. $args = apply_filters( 'acf/fields/taxonomy/query', $args, $field, $options['post_id'] );
  125. // get terms
  126. $terms = acf_get_terms( $args );
  127. // sort into hierachial order!
  128. if ( $is_hierarchical ) {
  129. // update vars
  130. $limit = acf_maybe_get( $args, 'number', $limit );
  131. $offset = acf_maybe_get( $args, 'offset', $offset );
  132. // get parent
  133. $parent = acf_maybe_get( $args, 'parent', 0 );
  134. $parent = acf_maybe_get( $args, 'child_of', $parent );
  135. // this will fail if a search has taken place because parents wont exist
  136. if ( ! $is_search ) {
  137. // order terms
  138. $ordered_terms = _get_term_children( $parent, $terms, $field['taxonomy'] );
  139. // check for empty array (possible if parent did not exist within original data)
  140. if ( ! empty( $ordered_terms ) ) {
  141. $terms = $ordered_terms;
  142. }
  143. }
  144. // fake pagination
  145. if ( $is_pagination ) {
  146. $terms = array_slice( $terms, $offset, $limit );
  147. }
  148. }
  149. // append to r
  150. foreach ( $terms as $term ) {
  151. // add to json
  152. $results[] = array(
  153. 'id' => $term->term_id,
  154. 'text' => $this->get_term_title( $term, $field, $options['post_id'] ),
  155. );
  156. }
  157. // vars
  158. $response = array(
  159. 'results' => $results,
  160. 'limit' => $limit,
  161. );
  162. // return
  163. return $response;
  164. }
  165. /**
  166. * Returns the Term's title displayed in the field UI.
  167. *
  168. * @date 1/11/2013
  169. * @since 5.0.0
  170. *
  171. * @param WP_Term $term The term object.
  172. * @param array $field The field settings.
  173. * @param mixed $post_id The post_id being edited.
  174. * @return string
  175. */
  176. function get_term_title( $term, $field, $post_id = 0 ) {
  177. $title = acf_get_term_title( $term );
  178. // Default $post_id to current post being edited.
  179. $post_id = $post_id ? $post_id : acf_get_form_data( 'post_id' );
  180. /**
  181. * Filters the term title.
  182. *
  183. * @date 1/11/2013
  184. * @since 5.0.0
  185. *
  186. * @param string $title The term title.
  187. * @param WP_Term $term The term object.
  188. * @param array $field The field settings.
  189. * @param (int|string) $post_id The post_id being edited.
  190. */
  191. return apply_filters( 'acf/fields/taxonomy/result', $title, $term, $field, $post_id );
  192. }
  193. /*
  194. * get_terms
  195. *
  196. * This function will return an array of terms for a given field value
  197. *
  198. * @type function
  199. * @date 13/06/2014
  200. * @since 5.0.0
  201. *
  202. * @param $value (array)
  203. * @return $value
  204. */
  205. function get_terms( $value, $taxonomy = 'category' ) {
  206. // load terms in 1 query to save multiple DB calls from following code
  207. if ( count( $value ) > 1 ) {
  208. $terms = acf_get_terms(
  209. array(
  210. 'taxonomy' => $taxonomy,
  211. 'include' => $value,
  212. 'hide_empty' => false,
  213. )
  214. );
  215. }
  216. // update value to include $post
  217. foreach ( array_keys( $value ) as $i ) {
  218. $value[ $i ] = get_term( $value[ $i ], $taxonomy );
  219. }
  220. // filter out null values
  221. $value = array_filter( $value );
  222. // return
  223. return $value;
  224. }
  225. /*
  226. * load_value()
  227. *
  228. * This filter is appied to the $value after it is loaded from the db
  229. *
  230. * @type filter
  231. * @since 3.6
  232. * @date 23/01/13
  233. *
  234. * @param $value - the value found in the database
  235. * @param $post_id - the $post_id from which the value was loaded from
  236. * @param $field - the field array holding all the field options
  237. *
  238. * @return $value - the value to be saved in te database
  239. */
  240. function load_value( $value, $post_id, $field ) {
  241. // get valid terms
  242. $value = acf_get_valid_terms( $value, $field['taxonomy'] );
  243. // load_terms
  244. if ( $field['load_terms'] ) {
  245. // Decode $post_id for $type and $id.
  246. $decoded = acf_decode_post_id( $post_id );
  247. $type = $decoded['type'];
  248. $id = $decoded['id'];
  249. if ( $type === 'block' ) {
  250. // Get parent block...
  251. }
  252. // get terms
  253. $term_ids = wp_get_object_terms(
  254. $id,
  255. $field['taxonomy'],
  256. array(
  257. 'fields' => 'ids',
  258. 'orderby' => 'none',
  259. )
  260. );
  261. // bail early if no terms
  262. if ( empty( $term_ids ) || is_wp_error( $term_ids ) ) {
  263. return false;
  264. }
  265. // sort
  266. if ( ! empty( $value ) ) {
  267. $order = array();
  268. foreach ( $term_ids as $i => $v ) {
  269. $order[ $i ] = array_search( $v, $value );
  270. }
  271. array_multisort( $order, $term_ids );
  272. }
  273. // update value
  274. $value = $term_ids;
  275. }
  276. // convert back from array if neccessary
  277. if ( $field['field_type'] == 'select' || $field['field_type'] == 'radio' ) {
  278. $value = array_shift( $value );
  279. }
  280. // return
  281. return $value;
  282. }
  283. /*
  284. * update_value()
  285. *
  286. * This filter is appied to the $value before it is updated in the db
  287. *
  288. * @type filter
  289. * @since 3.6
  290. * @date 23/01/13
  291. *
  292. * @param $value - the value which will be saved in the database
  293. * @param $field - the field array holding all the field options
  294. * @param $post_id - the $post_id of which the value will be saved
  295. *
  296. * @return $value - the modified value
  297. */
  298. function update_value( $value, $post_id, $field ) {
  299. // vars
  300. if ( is_array( $value ) ) {
  301. $value = array_filter( $value );
  302. }
  303. // save_terms
  304. if ( $field['save_terms'] ) {
  305. // vars
  306. $taxonomy = $field['taxonomy'];
  307. // force value to array
  308. $term_ids = acf_get_array( $value );
  309. // convert to int
  310. $term_ids = array_map( 'intval', $term_ids );
  311. // get existing term id's (from a previously saved field)
  312. $old_term_ids = isset( $this->save_post_terms[ $taxonomy ] ) ? $this->save_post_terms[ $taxonomy ] : array();
  313. // append
  314. $this->save_post_terms[ $taxonomy ] = array_merge( $old_term_ids, $term_ids );
  315. // if called directly from frontend update_field()
  316. if ( ! did_action( 'acf/save_post' ) ) {
  317. $this->save_post( $post_id );
  318. return $value;
  319. }
  320. }
  321. // return
  322. return $value;
  323. }
  324. /**
  325. * This function will save any terms in the save_post_terms array
  326. *
  327. * @date 26/11/2014
  328. * @since 5.0.9
  329. *
  330. * @param int $post_id
  331. *
  332. * @return void
  333. */
  334. function save_post( $post_id ) {
  335. // Check for saved terms.
  336. if ( ! empty( $this->save_post_terms ) ) {
  337. /**
  338. * Determine object ID allowing for non "post" $post_id (user, taxonomy, etc).
  339. * Although not fully supported by WordPress, non "post" objects may use the term relationships table.
  340. * Sharing taxonomies across object types is discouraged, but unique taxonomies work well.
  341. * Note: Do not attempt to restrict to "post" only. This has been attempted in 5.8.9 and later reverted.
  342. */
  343. $decoded = acf_decode_post_id( $post_id );
  344. $type = $decoded['type'];
  345. $id = $decoded['id'];
  346. if ( $type === 'block' ) {
  347. // Get parent block...
  348. }
  349. // Loop over taxonomies and save terms.
  350. foreach ( $this->save_post_terms as $taxonomy => $term_ids ) {
  351. wp_set_object_terms( $id, $term_ids, $taxonomy, false );
  352. }
  353. // Reset storage.
  354. $this->save_post_terms = array();
  355. }
  356. }
  357. /*
  358. * format_value()
  359. *
  360. * This filter is appied to the $value after it is loaded from the db and before it is returned to the template
  361. *
  362. * @type filter
  363. * @since 3.6
  364. * @date 23/01/13
  365. *
  366. * @param $value (mixed) the value which was loaded from the database
  367. * @param $post_id (mixed) the $post_id from which the value was loaded
  368. * @param $field (array) the field array holding all the field options
  369. *
  370. * @return $value (mixed) the modified value
  371. */
  372. function format_value( $value, $post_id, $field ) {
  373. // bail early if no value
  374. if ( empty( $value ) ) {
  375. return false;
  376. }
  377. // force value to array
  378. $value = acf_get_array( $value );
  379. // load posts if needed
  380. if ( $field['return_format'] == 'object' ) {
  381. // get posts
  382. $value = $this->get_terms( $value, $field['taxonomy'] );
  383. }
  384. // convert back from array if neccessary
  385. if ( $field['field_type'] == 'select' || $field['field_type'] == 'radio' ) {
  386. $value = array_shift( $value );
  387. }
  388. // return
  389. return $value;
  390. }
  391. /*
  392. * render_field()
  393. *
  394. * Create the HTML interface for your field
  395. *
  396. * @type action
  397. * @since 3.6
  398. * @date 23/01/13
  399. *
  400. * @param $field - an array holding all the field's data
  401. */
  402. function render_field( $field ) {
  403. // force value to array
  404. $field['value'] = acf_get_array( $field['value'] );
  405. // vars
  406. $div = array(
  407. 'class' => 'acf-taxonomy-field',
  408. 'data-save' => $field['save_terms'],
  409. 'data-ftype' => $field['field_type'],
  410. 'data-taxonomy' => $field['taxonomy'],
  411. 'data-allow_null' => $field['allow_null'],
  412. );
  413. // get taxonomy
  414. $taxonomy = get_taxonomy( $field['taxonomy'] );
  415. // bail early if taxonomy does not exist
  416. if ( ! $taxonomy ) {
  417. return;
  418. }
  419. ?>
  420. <div <?php echo acf_esc_attrs( $div ); ?>>
  421. <?php if ( $field['add_term'] && current_user_can( $taxonomy->cap->manage_terms ) ) : ?>
  422. <div class="acf-actions -hover">
  423. <a href="#" class="acf-icon -plus acf-js-tooltip small" data-name="add" title="<?php echo esc_attr( $taxonomy->labels->add_new_item ); ?>"></a>
  424. </div>
  425. <?php
  426. endif;
  427. if ( $field['field_type'] == 'select' ) {
  428. $field['multiple'] = 0;
  429. $this->render_field_select( $field );
  430. } elseif ( $field['field_type'] == 'multi_select' ) {
  431. $field['multiple'] = 1;
  432. $this->render_field_select( $field );
  433. } elseif ( $field['field_type'] == 'radio' ) {
  434. $this->render_field_checkbox( $field );
  435. } elseif ( $field['field_type'] == 'checkbox' ) {
  436. $this->render_field_checkbox( $field );
  437. }
  438. ?>
  439. </div>
  440. <?php
  441. }
  442. /*
  443. * render_field_select()
  444. *
  445. * Create the HTML interface for your field
  446. *
  447. * @type action
  448. * @since 3.6
  449. * @date 23/01/13
  450. *
  451. * @param $field - an array holding all the field's data
  452. */
  453. function render_field_select( $field ) {
  454. // Change Field into a select
  455. $field['type'] = 'select';
  456. $field['ui'] = 1;
  457. $field['ajax'] = 1;
  458. $field['choices'] = array();
  459. // value
  460. if ( ! empty( $field['value'] ) ) {
  461. // get terms
  462. $terms = $this->get_terms( $field['value'], $field['taxonomy'] );
  463. // set choices
  464. if ( ! empty( $terms ) ) {
  465. foreach ( array_keys( $terms ) as $i ) {
  466. // vars
  467. $term = acf_extract_var( $terms, $i );
  468. // append to choices
  469. $field['choices'][ $term->term_id ] = $this->get_term_title( $term, $field );
  470. }
  471. }
  472. }
  473. // render select
  474. acf_render_field( $field );
  475. }
  476. /**
  477. * Create the HTML interface for your field
  478. *
  479. * @since 3.6
  480. *
  481. * @param array $field an array holding all the field's data.
  482. */
  483. public function render_field_checkbox( $field ) {
  484. // hidden input.
  485. acf_hidden_input(
  486. array(
  487. 'type' => 'hidden',
  488. 'name' => $field['name'],
  489. )
  490. );
  491. // checkbox saves an array.
  492. if ( $field['field_type'] == 'checkbox' ) {
  493. $field['name'] .= '[]';
  494. }
  495. // taxonomy.
  496. $taxonomy_obj = get_taxonomy( $field['taxonomy'] );
  497. // include walker.
  498. acf_include( 'includes/walkers/class-acf-walker-taxonomy-field.php' );
  499. // vars.
  500. $args = array(
  501. 'taxonomy' => $field['taxonomy'],
  502. 'show_option_none' => sprintf( _x( 'No %s', 'No Terms', 'acf' ), $taxonomy_obj->labels->name ),
  503. 'hide_empty' => false,
  504. 'style' => 'none',
  505. 'walker' => new ACF_Taxonomy_Field_Walker( $field ),
  506. );
  507. // filter for 3rd party customization.
  508. $args = apply_filters( 'acf/fields/taxonomy/wp_list_categories', $args, $field );
  509. $args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/name=' . $field['_name'], $args, $field );
  510. $args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/key=' . $field['key'], $args, $field );
  511. ?>
  512. <div class="categorychecklist-holder">
  513. <ul class="acf-checkbox-list acf-bl">
  514. <?php wp_list_categories( $args ); ?>
  515. </ul>
  516. </div>
  517. <?php
  518. }
  519. /*
  520. * render_field_settings()
  521. *
  522. * Create extra options for your field. This is rendered when editing a field.
  523. * The value of $field['name'] can be used (like bellow) to save extra data to the $field
  524. *
  525. * @type action
  526. * @since 3.6
  527. * @date 23/01/13
  528. *
  529. * @param $field - an array holding all the field's data
  530. */
  531. function render_field_settings( $field ) {
  532. acf_render_field_setting(
  533. $field,
  534. array(
  535. 'label' => __( 'Taxonomy', 'acf' ),
  536. 'instructions' => __( 'Select the taxonomy to be displayed', 'acf' ),
  537. 'type' => 'select',
  538. 'name' => 'taxonomy',
  539. 'choices' => acf_get_taxonomy_labels(),
  540. )
  541. );
  542. acf_render_field_setting(
  543. $field,
  544. array(
  545. 'label' => __( 'Create Terms', 'acf' ),
  546. 'instructions' => __( 'Allow new terms to be created whilst editing', 'acf' ),
  547. 'name' => 'add_term',
  548. 'type' => 'true_false',
  549. 'ui' => 1,
  550. )
  551. );
  552. acf_render_field_setting(
  553. $field,
  554. array(
  555. 'label' => __( 'Save Terms', 'acf' ),
  556. 'instructions' => __( 'Connect selected terms to the post', 'acf' ),
  557. 'name' => 'save_terms',
  558. 'type' => 'true_false',
  559. 'ui' => 1,
  560. )
  561. );
  562. acf_render_field_setting(
  563. $field,
  564. array(
  565. 'label' => __( 'Load Terms', 'acf' ),
  566. 'instructions' => __( 'Load value from posts terms', 'acf' ),
  567. 'name' => 'load_terms',
  568. 'type' => 'true_false',
  569. 'ui' => 1,
  570. )
  571. );
  572. acf_render_field_setting(
  573. $field,
  574. array(
  575. 'label' => __( 'Return Value', 'acf' ),
  576. 'instructions' => '',
  577. 'type' => 'radio',
  578. 'name' => 'return_format',
  579. 'choices' => array(
  580. 'object' => __( 'Term Object', 'acf' ),
  581. 'id' => __( 'Term ID', 'acf' ),
  582. ),
  583. 'layout' => 'horizontal',
  584. )
  585. );
  586. acf_render_field_setting(
  587. $field,
  588. array(
  589. 'label' => __( 'Appearance', 'acf' ),
  590. 'instructions' => __( 'Select the appearance of this field', 'acf' ),
  591. 'type' => 'select',
  592. 'name' => 'field_type',
  593. 'optgroup' => true,
  594. 'choices' => array(
  595. __( 'Multiple Values', 'acf' ) => array(
  596. 'checkbox' => __( 'Checkbox', 'acf' ),
  597. 'multi_select' => __( 'Multi Select', 'acf' ),
  598. ),
  599. __( 'Single Value', 'acf' ) => array(
  600. 'radio' => __( 'Radio Buttons', 'acf' ),
  601. 'select' => _x( 'Select', 'noun', 'acf' ),
  602. ),
  603. ),
  604. )
  605. );
  606. acf_render_field_setting(
  607. $field,
  608. array(
  609. 'label' => __( 'Allow Null?', 'acf' ),
  610. 'instructions' => '',
  611. 'name' => 'allow_null',
  612. 'type' => 'true_false',
  613. 'ui' => 1,
  614. 'conditions' => array(
  615. 'field' => 'field_type',
  616. 'operator' => '!=',
  617. 'value' => 'checkbox',
  618. ),
  619. )
  620. );
  621. }
  622. /*
  623. * ajax_add_term
  624. *
  625. * description
  626. *
  627. * @type function
  628. * @date 17/04/2015
  629. * @since 5.2.3
  630. *
  631. * @param $post_id (int)
  632. * @return $post_id (int)
  633. */
  634. function ajax_add_term() {
  635. // verify nonce
  636. if ( ! acf_verify_ajax() ) {
  637. die();
  638. }
  639. // vars
  640. $args = wp_parse_args(
  641. $_POST,
  642. array(
  643. 'nonce' => '',
  644. 'field_key' => '',
  645. 'term_name' => '',
  646. 'term_parent' => '',
  647. )
  648. );
  649. // load field
  650. $field = acf_get_field( $args['field_key'] );
  651. if ( ! $field ) {
  652. die();
  653. }
  654. // vars
  655. $taxonomy_obj = get_taxonomy( $field['taxonomy'] );
  656. $taxonomy_label = $taxonomy_obj->labels->singular_name;
  657. // validate cap
  658. // note: this situation should never occur due to condition of the add new button
  659. if ( ! current_user_can( $taxonomy_obj->cap->manage_terms ) ) {
  660. wp_send_json_error(
  661. array(
  662. 'error' => sprintf( __( 'User unable to add new %s', 'acf' ), $taxonomy_label ),
  663. )
  664. );
  665. }
  666. // save?
  667. if ( $args['term_name'] ) {
  668. // exists
  669. if ( term_exists( $args['term_name'], $field['taxonomy'], $args['term_parent'] ) ) {
  670. wp_send_json_error(
  671. array(
  672. 'error' => sprintf( __( '%s already exists', 'acf' ), $taxonomy_label ),
  673. )
  674. );
  675. }
  676. // vars
  677. $extra = array();
  678. if ( $args['term_parent'] ) {
  679. $extra['parent'] = (int) $args['term_parent'];
  680. }
  681. // insert
  682. $data = wp_insert_term( $args['term_name'], $field['taxonomy'], $extra );
  683. // error
  684. if ( is_wp_error( $data ) ) {
  685. wp_send_json_error(
  686. array(
  687. 'error' => $data->get_error_message(),
  688. )
  689. );
  690. }
  691. // load term
  692. $term = get_term( $data['term_id'] );
  693. // prepend ancenstors count to term name
  694. $prefix = '';
  695. $ancestors = get_ancestors( $term->term_id, $term->taxonomy );
  696. if ( ! empty( $ancestors ) ) {
  697. $prefix = str_repeat( '- ', count( $ancestors ) );
  698. }
  699. // success
  700. wp_send_json_success(
  701. array(
  702. 'message' => sprintf( __( '%s added', 'acf' ), $taxonomy_label ),
  703. 'term_id' => $term->term_id,
  704. 'term_name' => $term->name,
  705. 'term_label' => $prefix . $term->name,
  706. 'term_parent' => $term->parent,
  707. )
  708. );
  709. }
  710. ?>
  711. <form method="post">
  712. <?php
  713. acf_render_field_wrap(
  714. array(
  715. 'label' => __( 'Name', 'acf' ),
  716. 'name' => 'term_name',
  717. 'type' => 'text',
  718. )
  719. );
  720. if ( is_taxonomy_hierarchical( $field['taxonomy'] ) ) {
  721. $choices = array();
  722. $response = $this->get_ajax_query( $args );
  723. if ( $response ) {
  724. foreach ( $response['results'] as $v ) {
  725. $choices[ $v['id'] ] = $v['text'];
  726. }
  727. }
  728. acf_render_field_wrap(
  729. array(
  730. 'label' => __( 'Parent', 'acf' ),
  731. 'name' => 'term_parent',
  732. 'type' => 'select',
  733. 'allow_null' => 1,
  734. 'ui' => 0,
  735. 'choices' => $choices,
  736. )
  737. );
  738. }
  739. ?>
  740. <p class="acf-submit">
  741. <button class="acf-submit-button button button-primary" type="submit"><?php _e( 'Add', 'acf' ); ?></button>
  742. </p>
  743. </form><?php
  744. // die
  745. die;
  746. }
  747. /**
  748. * Return the schema array for the REST API.
  749. *
  750. * @param array $field
  751. * @return array
  752. */
  753. public function get_rest_schema( array $field ) {
  754. $schema = array(
  755. 'type' => array( 'integer', 'array', 'null' ),
  756. 'required' => ! empty( $field['required'] ),
  757. 'items' => array(
  758. 'type' => 'integer',
  759. ),
  760. );
  761. if ( empty( $field['allow_null'] ) ) {
  762. $schema['minItems'] = 1;
  763. }
  764. if ( in_array( $field['field_type'], array( 'radio', 'select' ) ) ) {
  765. $schema['maxItems'] = 1;
  766. }
  767. return $schema;
  768. }
  769. /**
  770. * @see \acf_field::get_rest_links()
  771. * @param mixed $value The raw (unformatted) field value.
  772. * @param int|string $post_id
  773. * @param array $field
  774. * @return array
  775. */
  776. public function get_rest_links( $value, $post_id, array $field ) {
  777. $links = array();
  778. if ( empty( $value ) ) {
  779. return $links;
  780. }
  781. foreach ( (array) $value as $object_id ) {
  782. $term = get_term( $object_id );
  783. if ( ! $term instanceof WP_Term ) {
  784. continue;
  785. }
  786. $rest_base = acf_get_object_type_rest_base( get_taxonomy( $term->taxonomy ) );
  787. if ( ! $rest_base ) {
  788. continue;
  789. }
  790. $links[] = array(
  791. 'rel' => 'acf:term',
  792. 'href' => rest_url( sprintf( '/wp/v2/%s/%s', $rest_base, $object_id ) ),
  793. 'embeddable' => true,
  794. 'taxonomy' => $term->taxonomy,
  795. );
  796. }
  797. return $links;
  798. }
  799. }
  800. // initialize
  801. acf_register_field_type( 'acf_field_taxonomy' );
  802. endif; // class_exists check
  803. ?>