Нет описания

class-acf-field-taxonomy.php 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  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 acf_esc_attr_e( $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. * render_field_checkbox()
  478. *
  479. * Create the HTML interface for your field
  480. *
  481. * @type action
  482. * @since 3.6
  483. * @date 23/01/13
  484. *
  485. * @param $field - an array holding all the field's data
  486. */
  487. function render_field_checkbox( $field ) {
  488. // hidden input
  489. acf_hidden_input(
  490. array(
  491. 'type' => 'hidden',
  492. 'name' => $field['name'],
  493. )
  494. );
  495. // checkbox saves an array
  496. if ( $field['field_type'] == 'checkbox' ) {
  497. $field['name'] .= '[]';
  498. }
  499. // taxonomy
  500. $taxonomy_obj = get_taxonomy( $field['taxonomy'] );
  501. // include walker
  502. acf_include( 'includes/walkers/class-acf-walker-taxonomy-field.php' );
  503. // vars
  504. $args = array(
  505. 'taxonomy' => $field['taxonomy'],
  506. 'show_option_none' => sprintf( _x( 'No %s', 'No terms', 'acf' ), strtolower( $taxonomy_obj->labels->name ) ),
  507. 'hide_empty' => false,
  508. 'style' => 'none',
  509. 'walker' => new ACF_Taxonomy_Field_Walker( $field ),
  510. );
  511. // filter for 3rd party customization
  512. $args = apply_filters( 'acf/fields/taxonomy/wp_list_categories', $args, $field );
  513. $args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/name=' . $field['_name'], $args, $field );
  514. $args = apply_filters( 'acf/fields/taxonomy/wp_list_categories/key=' . $field['key'], $args, $field );
  515. ?>
  516. <div class="categorychecklist-holder">
  517. <ul class="acf-checkbox-list acf-bl">
  518. <?php wp_list_categories( $args ); ?>
  519. </ul>
  520. </div>
  521. <?php
  522. }
  523. /*
  524. * render_field_settings()
  525. *
  526. * Create extra options for your field. This is rendered when editing a field.
  527. * The value of $field['name'] can be used (like bellow) to save extra data to the $field
  528. *
  529. * @type action
  530. * @since 3.6
  531. * @date 23/01/13
  532. *
  533. * @param $field - an array holding all the field's data
  534. */
  535. function render_field_settings( $field ) {
  536. // default_value
  537. acf_render_field_setting(
  538. $field,
  539. array(
  540. 'label' => __( 'Taxonomy', 'acf' ),
  541. 'instructions' => __( 'Select the taxonomy to be displayed', 'acf' ),
  542. 'type' => 'select',
  543. 'name' => 'taxonomy',
  544. 'choices' => acf_get_taxonomy_labels(),
  545. )
  546. );
  547. // field_type
  548. acf_render_field_setting(
  549. $field,
  550. array(
  551. 'label' => __( 'Appearance', 'acf' ),
  552. 'instructions' => __( 'Select the appearance of this field', 'acf' ),
  553. 'type' => 'select',
  554. 'name' => 'field_type',
  555. 'optgroup' => true,
  556. 'choices' => array(
  557. __( 'Multiple Values', 'acf' ) => array(
  558. 'checkbox' => __( 'Checkbox', 'acf' ),
  559. 'multi_select' => __( 'Multi Select', 'acf' ),
  560. ),
  561. __( 'Single Value', 'acf' ) => array(
  562. 'radio' => __( 'Radio Buttons', 'acf' ),
  563. 'select' => _x( 'Select', 'noun', 'acf' ),
  564. ),
  565. ),
  566. )
  567. );
  568. // allow_null
  569. acf_render_field_setting(
  570. $field,
  571. array(
  572. 'label' => __( 'Allow Null?', 'acf' ),
  573. 'instructions' => '',
  574. 'name' => 'allow_null',
  575. 'type' => 'true_false',
  576. 'ui' => 1,
  577. 'conditions' => array(
  578. 'field' => 'field_type',
  579. 'operator' => '!=',
  580. 'value' => 'checkbox',
  581. ),
  582. )
  583. );
  584. // add_term
  585. acf_render_field_setting(
  586. $field,
  587. array(
  588. 'label' => __( 'Create Terms', 'acf' ),
  589. 'instructions' => __( 'Allow new terms to be created whilst editing', 'acf' ),
  590. 'name' => 'add_term',
  591. 'type' => 'true_false',
  592. 'ui' => 1,
  593. )
  594. );
  595. // save_terms
  596. acf_render_field_setting(
  597. $field,
  598. array(
  599. 'label' => __( 'Save Terms', 'acf' ),
  600. 'instructions' => __( 'Connect selected terms to the post', 'acf' ),
  601. 'name' => 'save_terms',
  602. 'type' => 'true_false',
  603. 'ui' => 1,
  604. )
  605. );
  606. // load_terms
  607. acf_render_field_setting(
  608. $field,
  609. array(
  610. 'label' => __( 'Load Terms', 'acf' ),
  611. 'instructions' => __( 'Load value from posts terms', 'acf' ),
  612. 'name' => 'load_terms',
  613. 'type' => 'true_false',
  614. 'ui' => 1,
  615. )
  616. );
  617. // return_format
  618. acf_render_field_setting(
  619. $field,
  620. array(
  621. 'label' => __( 'Return Value', 'acf' ),
  622. 'instructions' => '',
  623. 'type' => 'radio',
  624. 'name' => 'return_format',
  625. 'choices' => array(
  626. 'object' => __( 'Term Object', 'acf' ),
  627. 'id' => __( 'Term ID', 'acf' ),
  628. ),
  629. 'layout' => 'horizontal',
  630. )
  631. );
  632. }
  633. /*
  634. * ajax_add_term
  635. *
  636. * description
  637. *
  638. * @type function
  639. * @date 17/04/2015
  640. * @since 5.2.3
  641. *
  642. * @param $post_id (int)
  643. * @return $post_id (int)
  644. */
  645. function ajax_add_term() {
  646. // vars
  647. $args = wp_parse_args(
  648. $_POST,
  649. array(
  650. 'nonce' => '',
  651. 'field_key' => '',
  652. 'term_name' => '',
  653. 'term_parent' => '',
  654. )
  655. );
  656. // verify nonce
  657. if ( ! acf_verify_ajax() ) {
  658. die();
  659. }
  660. // load field
  661. $field = acf_get_field( $args['field_key'] );
  662. if ( ! $field ) {
  663. die();
  664. }
  665. // vars
  666. $taxonomy_obj = get_taxonomy( $field['taxonomy'] );
  667. $taxonomy_label = $taxonomy_obj->labels->singular_name;
  668. // validate cap
  669. // note: this situation should never occur due to condition of the add new button
  670. if ( ! current_user_can( $taxonomy_obj->cap->manage_terms ) ) {
  671. wp_send_json_error(
  672. array(
  673. 'error' => sprintf( __( 'User unable to add new %s', 'acf' ), $taxonomy_label ),
  674. )
  675. );
  676. }
  677. // save?
  678. if ( $args['term_name'] ) {
  679. // exists
  680. if ( term_exists( $args['term_name'], $field['taxonomy'], $args['term_parent'] ) ) {
  681. wp_send_json_error(
  682. array(
  683. 'error' => sprintf( __( '%s already exists', 'acf' ), $taxonomy_label ),
  684. )
  685. );
  686. }
  687. // vars
  688. $extra = array();
  689. if ( $args['term_parent'] ) {
  690. $extra['parent'] = (int) $args['term_parent'];
  691. }
  692. // insert
  693. $data = wp_insert_term( $args['term_name'], $field['taxonomy'], $extra );
  694. // error
  695. if ( is_wp_error( $data ) ) {
  696. wp_send_json_error(
  697. array(
  698. 'error' => $data->get_error_message(),
  699. )
  700. );
  701. }
  702. // load term
  703. $term = get_term( $data['term_id'] );
  704. // prepend ancenstors count to term name
  705. $prefix = '';
  706. $ancestors = get_ancestors( $term->term_id, $term->taxonomy );
  707. if ( ! empty( $ancestors ) ) {
  708. $prefix = str_repeat( '- ', count( $ancestors ) );
  709. }
  710. // success
  711. wp_send_json_success(
  712. array(
  713. 'message' => sprintf( __( '%s added', 'acf' ), $taxonomy_label ),
  714. 'term_id' => $term->term_id,
  715. 'term_name' => $term->name,
  716. 'term_label' => $prefix . $term->name,
  717. 'term_parent' => $term->parent,
  718. )
  719. );
  720. }
  721. ?>
  722. <form method="post">
  723. <?php
  724. acf_render_field_wrap(
  725. array(
  726. 'label' => __( 'Name', 'acf' ),
  727. 'name' => 'term_name',
  728. 'type' => 'text',
  729. )
  730. );
  731. if ( is_taxonomy_hierarchical( $field['taxonomy'] ) ) {
  732. $choices = array();
  733. $response = $this->get_ajax_query( $args );
  734. if ( $response ) {
  735. foreach ( $response['results'] as $v ) {
  736. $choices[ $v['id'] ] = $v['text'];
  737. }
  738. }
  739. acf_render_field_wrap(
  740. array(
  741. 'label' => __( 'Parent', 'acf' ),
  742. 'name' => 'term_parent',
  743. 'type' => 'select',
  744. 'allow_null' => 1,
  745. 'ui' => 0,
  746. 'choices' => $choices,
  747. )
  748. );
  749. }
  750. ?>
  751. <p class="acf-submit">
  752. <button class="acf-submit-button button button-primary" type="submit"><?php _e( 'Add', 'acf' ); ?></button>
  753. </p>
  754. </form><?php
  755. // die
  756. die;
  757. }
  758. /**
  759. * Return the schema array for the REST API.
  760. *
  761. * @param array $field
  762. * @return array
  763. */
  764. public function get_rest_schema( array $field ) {
  765. $schema = array(
  766. 'type' => array( 'integer', 'array', 'null' ),
  767. 'required' => ! empty( $field['required'] ),
  768. 'items' => array(
  769. 'type' => 'integer',
  770. ),
  771. );
  772. if ( empty( $field['allow_null'] ) ) {
  773. $schema['minItems'] = 1;
  774. }
  775. if ( in_array( $field['field_type'], array( 'radio', 'select' ) ) ) {
  776. $schema['maxItems'] = 1;
  777. }
  778. return $schema;
  779. }
  780. /**
  781. * @see \acf_field::get_rest_links()
  782. * @param mixed $value The raw (unformatted) field value.
  783. * @param int|string $post_id
  784. * @param array $field
  785. * @return array
  786. */
  787. public function get_rest_links( $value, $post_id, array $field ) {
  788. $links = array();
  789. if ( empty( $value ) ) {
  790. return $links;
  791. }
  792. foreach ( (array) $value as $object_id ) {
  793. $term = get_term( $object_id );
  794. if ( ! $term instanceof WP_Term ) {
  795. continue;
  796. }
  797. $rest_base = acf_get_object_type_rest_base( get_taxonomy( $term->taxonomy ) );
  798. if ( ! $rest_base ) {
  799. continue;
  800. }
  801. $links[] = array(
  802. 'rel' => 'acf:term',
  803. 'href' => rest_url( sprintf( '/wp/v2/%s/%s', $rest_base, $object_id ) ),
  804. 'embeddable' => true,
  805. 'taxonomy' => $term->taxonomy,
  806. );
  807. }
  808. return $links;
  809. }
  810. }
  811. // initialize
  812. acf_register_field_type( 'acf_field_taxonomy' );
  813. endif; // class_exists check
  814. ?>