PluginProbe
Polylang / 3.8.6
Polylang v3.8.6
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / src / admin / admin-filters-term.php

admin-filters-term.php in Polylang 3.8.6, at src/admin/admin-filters-term.php

735 lines 23.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 use WP_Syntex\Polylang\Capabilities\Capabilities;
7
8 defined( 'ABSPATH' ) || exit;
9
10 /**
11 * Manages filters and actions related to terms on admin side
12 *
13 * @since 1.2
14 */
15 class PLL_Admin_Filters_Term {
16 /**
17 * @var PLL_Model
18 */
19 public $model;
20
21 /**
22 * @var PLL_Admin_Links
23 */
24 public $links;
25
26 /**
27 * Language selected in the admin language filter.
28 *
29 * @var PLL_Language
30 */
31 public $filter_lang;
32
33 /**
34 * Preferred language to assign to the new terms.
35 *
36 * @var PLL_Language
37 */
38 public $pref_lang;
39
40 /**
41 * Stores the current post_id when bulk editing posts.
42 *
43 * @var int
44 */
45 protected $post_id = 0;
46
47 /**
48 * A reference to the PLL_Admin_Default_Term instance.
49 *
50 * @since 2.8
51 *
52 * @var PLL_Admin_Default_Term|null
53 */
54 protected $default_term;
55
56 /**
57 * Constructor: setups filters and actions.
58 *
59 * @since 1.2
60 *
61 * @param object $polylang The Polylang object.
62 */
63 public function __construct( &$polylang ) {
64 $this->links = &$polylang->links;
65 $this->model = &$polylang->model;
66 $this->pref_lang = &$polylang->pref_lang;
67 $this->default_term = &$polylang->default_term;
68
69 foreach ( $this->model->get_translated_taxonomies() as $tax ) {
70 // Adds the language field in the 'Categories' and 'Post Tags' panels
71 add_action( $tax . '_add_form_fields', array( $this, 'add_term_form' ) );
72
73 // Adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels
74 add_action( $tax . '_edit_form_fields', array( $this, 'edit_term_form' ) );
75 }
76
77 // Adds actions related to languages when creating or saving categories and post tags
78 add_filter( 'wp_dropdown_cats', array( $this, 'wp_dropdown_cats' ) );
79 add_action( 'create_term', array( $this, 'save_term' ), 900, 3 );
80 add_action( 'edit_term', array( $this, 'save_term' ), 900, 3 ); // Late as it may conflict with other plugins, see http://wordpress.org/support/topic/polylang-and-wordpress-seo-by-yoast
81 add_action( 'pre_post_update', array( $this, 'pre_post_update' ) );
82 add_filter( 'pll_inserted_term_language', array( $this, 'get_inserted_term_language' ) );
83 add_filter( 'pll_inserted_term_parent', array( $this, 'get_inserted_term_parent' ), 10, 2 );
84
85 // Ajax response for edit term form
86 add_action( 'wp_ajax_term_lang_choice', array( $this, 'term_lang_choice' ) );
87 add_action( 'wp_ajax_pll_terms_not_translated', array( $this, 'ajax_terms_not_translated' ) );
88
89 // Updates the translations term ids when splitting a shared term
90 add_action( 'split_shared_term', array( $this, 'split_shared_term' ), 10, 4 ); // WP 4.2
91 }
92
93 /**
94 * Adds the language field in the 'Categories' and 'Post Tags' panels
95 *
96 * @since 0.1
97 *
98 * @return void
99 */
100 public function add_term_form() {
101 if ( isset( $_GET['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
102 $taxonomy = sanitize_key( $_GET['taxonomy'] ); // phpcs:ignore WordPress.Security.NonceVerification
103 }
104
105 if ( isset( $_REQUEST['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
106 $post_type = sanitize_key( $_REQUEST['post_type'] ); // phpcs:ignore WordPress.Security.NonceVerification
107 }
108
109 if ( isset( $GLOBALS['post_type'] ) ) {
110 $post_type = $GLOBALS['post_type'];
111 }
112
113 if ( ! isset( $taxonomy, $post_type ) || ! taxonomy_exists( $taxonomy ) || ! post_type_exists( $post_type ) ) {
114 return;
115 }
116
117 $from_term_id = isset( $_GET['from_tag'] ) ? (int) $_GET['from_tag'] : 0; // phpcs:ignore WordPress.Security.NonceVerification
118
119 $lang = isset( $_GET['new_lang'] ) ? $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ) : $this->pref_lang; // phpcs:ignore WordPress.Security.NonceVerification
120
121 $dropdown = new PLL_Walker_Dropdown();
122
123 $dropdown_html = $dropdown->walk(
124 $this->model->languages->filter( 'translator' )->get_list(),
125 -1,
126 array(
127 'name' => 'term_lang_choice',
128 'value' => 'term_id',
129 'selected' => $lang ? $lang->term_id : '',
130 'flag' => true,
131 )
132 );
133
134 wp_nonce_field( 'pll_language', '_pll_nonce' );
135
136 printf(
137 '<div class="form-field">
138 <label for="term_lang_choice">%s</label>
139 <div id="select-add-term-language">%s</div>
140 <p>%s</p>
141 </div>',
142 esc_html__( 'Language', 'polylang' ),
143 $dropdown_html, // phpcs:ignore
144 esc_html__( 'Sets the language', 'polylang' )
145 );
146
147 if ( ! empty( $from_term_id ) ) {
148 printf( '<input type="hidden" name="from_tag" value="%d" />', (int) $from_term_id );
149 }
150
151 // Adds translation fields
152 echo '<div id="term-translations" class="form-field">';
153 if ( $lang ) {
154 include __DIR__ . '/view-translations-term.php';
155 }
156 echo '</div>' . "\n";
157 }
158
159 /**
160 * Adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels.
161 *
162 * @since 0.1
163 *
164 * @param WP_Term $term The term being edited.
165 * @return void
166 */
167 public function edit_term_form( $term ) {
168 if ( isset( $_REQUEST['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
169 $post_type = sanitize_key( $_REQUEST['post_type'] ); // phpcs:ignore WordPress.Security.NonceVerification
170 }
171
172 if ( isset( $GLOBALS['post_type'] ) ) {
173 $post_type = $GLOBALS['post_type'];
174 }
175
176 if ( ! isset( $post_type ) || ! post_type_exists( $post_type ) ) {
177 return;
178 }
179
180 $taxonomy = $term->taxonomy; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
181
182 $lang = $this->model->term->get_language( $term->term_id );
183 $lang = empty( $lang ) ? $this->pref_lang : $lang;
184
185 // Disable the language dropdown and the translations input fields for default terms to prevent removal
186 $disabled = $this->default_term->is_default_term( $term->term_id );
187
188 $dropdown = new PLL_Walker_Dropdown();
189
190 $dropdown_html = $dropdown->walk(
191 $this->model->languages->filter( 'translator' )->get_list(),
192 -1,
193 array(
194 'name' => 'term_lang_choice',
195 'value' => 'term_id',
196 'selected' => $lang->term_id,
197 'disabled' => $disabled,
198 'flag' => true,
199 )
200 );
201
202 wp_nonce_field( 'pll_language', '_pll_nonce' );
203
204 printf(
205 '<tr class="form-field">
206 <th scope="row">
207 <label for="term_lang_choice">%s</label>
208 </th>
209 <td id="select-edit-term-language">
210 %s<br />
211 <p class="description">%s</p>
212 </td>
213 </tr>',
214 esc_html__( 'Language', 'polylang' ),
215 $dropdown_html, // phpcs:ignore
216 esc_html__( 'Sets the language', 'polylang' )
217 );
218
219 echo '<tr id="term-translations" class="form-field">';
220 include __DIR__ . '/view-translations-term.php';
221 echo '</tr>' . "\n";
222 }
223
224 /**
225 * Translates term parent if exists when using "Add new" ( translation )
226 *
227 * @since 0.7
228 *
229 * @param string $output html markup for dropdown list of categories
230 * @return string modified html
231 */
232 public function wp_dropdown_cats( $output ) {
233 if ( isset( $_GET['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
234 $taxonomy = sanitize_key( $_GET['taxonomy'] ); // phpcs:ignore WordPress.Security.NonceVerification
235 }
236
237 if ( isset( $taxonomy, $_GET['from_tag'], $_GET['new_lang'] ) && taxonomy_exists( $taxonomy ) ) { // phpcs:ignore WordPress.Security.NonceVerification
238 $term = get_term( (int) $_GET['from_tag'], $taxonomy ); // phpcs:ignore WordPress.Security.NonceVerification
239
240 if ( $term instanceof WP_Term && $id = $term->parent ) {
241 $lang = $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
242 if ( $parent = $this->model->term->get_translation( $id, $lang ) ) {
243 return str_replace( '"' . $parent . '"', '"' . $parent . '" selected="selected"', $output );
244 }
245 }
246 }
247 return $output;
248 }
249
250 /**
251 * Stores the current post_id when bulk editing posts for use in save_language and get_inserted_term_language.
252 *
253 * @since 1.7
254 *
255 * @param int $post_id Post ID.
256 * @return void
257 */
258 public function pre_post_update( $post_id ) {
259 if ( isset( $_GET['bulk_edit'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
260 $this->post_id = $post_id;
261 }
262 }
263
264 /**
265 * Saves the language of a term.
266 *
267 * @since 1.5
268 *
269 * @param int $term_id The term ID.
270 * @param string $taxonomy Taxonomy name.
271 * @return void|never
272 */
273 protected function save_language( $term_id, $taxonomy ) {
274 $term_id = (int) $term_id;
275
276 /*
277 * Category metabox.
278 */
279 if ( isset( $_POST['term_lang_choice'], $_REQUEST[ '_ajax_nonce-add-' . $taxonomy ] ) && wp_verify_nonce( $_REQUEST[ '_ajax_nonce-add-' . $taxonomy ], 'add-' . $taxonomy ) ) {
280 $this->maybe_set_language( $term_id, sanitize_key( $_POST['term_lang_choice'] ) );
281 return;
282 }
283
284 /*
285 * Edit tags or tags metabox.
286 */
287 if ( isset( $_POST['term_lang_choice'] ) ) {
288 check_admin_referer( 'pll_language', '_pll_nonce' );
289 $this->maybe_set_language( $term_id, sanitize_key( $_POST['term_lang_choice'] ) );
290 return;
291 }
292
293 /*
294 * *Post* bulk edit, in case a new term is created.
295 */
296 if ( isset( $_GET['bulk_edit'], $_GET['inline_lang_choice'], $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-posts' ) ) {
297 /*
298 * Bulk edit does not modify the language, so we possibly create a tag in several languages.
299 */
300 if ( -1 === (int) $_GET['inline_lang_choice'] ) {
301 // The language of the current term is set a according to the language of the current post.
302 $language = $this->model->post->get_language( $this->post_id );
303
304 if ( empty( $language ) ) {
305 return;
306 }
307
308 Capabilities::get_user()->can_translate_or_die( $language );
309
310 $this->model->term->set_language( $term_id, $language );
311 $term = get_term( $term_id, $taxonomy );
312
313 // Get all terms with the same name.
314 if ( $term instanceof WP_Term ) {
315 $term_ids = get_terms( array( 'taxonomy' => $taxonomy, 'name' => $term->name, 'hide_empty' => false, 'fields' => 'ids' ) );
316
317 // If we have several terms with the same name, they are translations of each other.
318 if ( is_array( $term_ids ) && count( $term_ids ) > 1 ) {
319 $translations = array();
320
321 foreach ( $term_ids as $_id ) {
322 $translations[ $this->model->term->get_language( $_id )->slug ] = $_id;
323 }
324
325 $this->model->term->save_translations( $term_id, $translations );
326 }
327 }
328 return;
329 }
330
331 if ( current_user_can( 'edit_term', $term_id ) ) {
332 $this->maybe_set_language( $term_id, sanitize_key( $_GET['inline_lang_choice'] ) );
333 }
334 return;
335 }
336
337 /*
338 * Quick edit.
339 */
340 if ( isset( $_POST['inline_lang_choice'], $_REQUEST['_inline_edit'] ) ) {
341 if ( ! wp_verify_nonce( $_REQUEST['_inline_edit'], 'inlineeditnonce' ) && ! wp_verify_nonce( $_REQUEST['_inline_edit'], 'taxinlineeditnonce' ) ) { // Post quick edit or tag quick edit?
342 return;
343 }
344
345 $this->maybe_set_language( $term_id, sanitize_key( $_POST['inline_lang_choice'] ) );
346 return;
347 }
348
349 /*
350 * Edit post.
351 */
352 if ( isset( $_POST['post_lang_choice'] ) ) { // FIXME should be useless now.
353 check_admin_referer( 'pll_language', '_pll_nonce' );
354 $this->maybe_set_language( $term_id, sanitize_key( $_POST['post_lang_choice'] ) );
355 }
356 }
357
358 /**
359 * Sets the language of a term if the user is allowed to.
360 *
361 * @since 3.8
362 *
363 * @param int $term_id The term ID.
364 * @param string $language Language slug.
365 * @return void|never
366 */
367 private function maybe_set_language( int $term_id, string $language ): void {
368 $language = $this->model->get_language( $language );
369
370 if ( empty( $language ) ) {
371 return;
372 }
373
374 Capabilities::get_user()->can_translate_or_die( $language );
375
376 $this->model->term->set_language( $term_id, $language );
377 }
378
379 /**
380 * Save translations from our form.
381 *
382 * @since 1.5
383 *
384 * @param int $term_id The term id of the term being saved.
385 * @return int[] The array of translated term ids.
386 */
387 protected function save_translations( $term_id ) {
388 // Security check as 'wp_update_term' can be called from outside WP admin.
389 check_admin_referer( 'pll_language', '_pll_nonce' );
390
391 $translations = array();
392
393 // Save translations after checking the translated term is in the right language ( as well as cast id to int ).
394 if ( isset( $_POST['term_tr_lang'] ) ) {
395 foreach ( array_map( 'absint', $_POST['term_tr_lang'] ) as $lang => $tr_id ) {
396 $tr_lang = $this->model->term->get_language( $tr_id );
397 $translations[ $lang ] = $tr_lang && $tr_lang->slug == $lang ? $tr_id : 0;
398 }
399 }
400
401 $this->model->term->save_translations( $term_id, $translations );
402
403 return $translations;
404 }
405
406 /**
407 * Called when a category or post tag is created or edited
408 * Saves language and translations
409 *
410 * @since 0.1
411 *
412 * @param int $term_id Term ID.
413 * @param int $tt_id Term taxonomy ID.
414 * @param string $taxonomy Taxonomy name.
415 * @return void
416 */
417 public function save_term( $term_id, $tt_id, $taxonomy ) {
418 // Does nothing except on taxonomies which are filterable
419 if ( ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
420 return;
421 }
422
423 $tax = get_taxonomy( $taxonomy );
424
425 if ( empty( $tax ) ) {
426 return;
427 }
428
429 // Capability check
430 // As 'wp_update_term' can be called from outside WP admin
431 // 2nd test for creating tags when creating / editing a post
432 if ( current_user_can( $tax->cap->edit_terms ) || ( isset( $_POST['tax_input'][ $taxonomy ] ) && current_user_can( $tax->cap->assign_terms ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
433 $this->save_language( $term_id, $taxonomy );
434
435 if ( isset( $_POST['term_tr_lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
436 $this->save_translations( $term_id );
437 }
438 }
439 }
440
441 /**
442 * Ajax response for edit term form
443 *
444 * @since 0.2
445 *
446 * @return void
447 */
448 public function term_lang_choice() {
449 check_ajax_referer( 'pll_language', '_pll_nonce' );
450
451 if ( ! isset( $_POST['taxonomy'], $_POST['post_type'], $_POST['lang'] ) ) {
452 wp_die( 0 );
453 }
454
455 $lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
456 $taxonomy = sanitize_key( $_POST['taxonomy'] );
457 $post_type = sanitize_key( $_POST['post_type'] );
458
459 if ( empty( $lang ) || ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
460 wp_die( 0 );
461 }
462
463 if ( ! empty( $_POST['term_id'] ) ) {
464 $term = get_term( (int) $_POST['term_id'], $taxonomy ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
465 $term = $term instanceof WP_Term ? $term : null;
466 }
467
468 ob_start();
469 include __DIR__ . '/view-translations-term.php';
470 $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
471 ob_end_clean();
472
473 // Parent dropdown list ( only for hierarchical taxonomies )
474 // $args copied from edit_tags.php except echo
475 if ( is_taxonomy_hierarchical( $taxonomy ) ) {
476 $args = array(
477 'hide_empty' => 0,
478 'hide_if_empty' => false,
479 'taxonomy' => $taxonomy,
480 'name' => 'parent',
481 'orderby' => 'name',
482 'hierarchical' => true,
483 'show_option_none' => __( 'None', 'polylang' ),
484 'echo' => 0,
485 );
486 $x->Add( array( 'what' => 'parent', 'data' => wp_dropdown_categories( $args ) ) );
487 }
488
489 // Tag cloud
490 // Tests copied from edit_tags.php
491 else {
492 $tax = get_taxonomy( $taxonomy );
493 if ( ! empty( $tax ) && ! is_null( $tax->labels->popular_items ) ) {
494 $args = array( 'taxonomy' => $taxonomy, 'echo' => false );
495 if ( current_user_can( $tax->cap->edit_terms ) ) {
496 $args = array_merge( $args, array( 'link' => 'edit' ) );
497 }
498
499 $tag_cloud = wp_tag_cloud( $args );
500
501 if ( ! empty( $tag_cloud ) ) {
502 /** @phpstan-var non-falsy-string $tag_cloud */
503 $html = sprintf( '<div class="tagcloud"><h2>%1$s</h2>%2$s</div>', esc_html( $tax->labels->popular_items ), $tag_cloud );
504 $x->Add( array( 'what' => 'tag_cloud', 'data' => $html ) );
505 }
506 }
507 }
508
509 // Flag
510 $x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
511
512 $x->send();
513 }
514
515 /**
516 * Ajax response for input in translation autocomplete input box.
517 *
518 * @since 1.5
519 *
520 * @return void
521 */
522 public function ajax_terms_not_translated() {
523 check_ajax_referer( 'pll_language', '_pll_nonce' );
524
525 if ( ! isset( $_GET['term'], $_GET['post_type'], $_GET['taxonomy'], $_GET['term_language'], $_GET['translation_language'] ) ) {
526 wp_die( 0 );
527 }
528
529 /** @var string */
530 $s = wp_unslash( $_GET['term'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
531 $post_type = sanitize_key( $_GET['post_type'] );
532 $taxonomy = sanitize_key( $_GET['taxonomy'] );
533
534 if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
535 wp_die( 0 );
536 }
537
538 $term_language = $this->model->get_language( sanitize_key( $_GET['term_language'] ) );
539 $translation_language = $this->model->get_language( sanitize_key( $_GET['translation_language'] ) );
540
541 $terms = array();
542 $return = array();
543
544 // Add current translation in list.
545 // Not in add term as term_id is not set.
546 if ( isset( $_GET['term_id'] ) && 'undefined' !== $_GET['term_id'] && $term_id = $this->model->term->get_translation( (int) $_GET['term_id'], $translation_language ) ) {
547 $terms = array( get_term( $term_id, $taxonomy ) );
548 }
549
550 // It is more efficient to use one common query for all languages as soon as there are more than 2.
551 $all_terms = get_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => false, 'lang' => '', 'name__like' => $s ) );
552 if ( is_array( $all_terms ) ) {
553 foreach ( $all_terms as $term ) {
554 $lang = $this->model->term->get_language( $term->term_id );
555
556 if ( $lang && $lang->slug == $translation_language->slug && ! $this->model->term->get_translation( $term->term_id, $term_language ) ) {
557 $terms[] = $term;
558 }
559 }
560 }
561
562 // Format the ajax response.
563 foreach ( $terms as $term ) {
564 if ( ! $term instanceof WP_Term ) {
565 continue;
566 }
567
568 $parents_list = get_term_parents_list(
569 $term->term_id,
570 $term->taxonomy,
571 array(
572 'separator' => ' > ',
573 'link' => false,
574 )
575 );
576
577 if ( ! is_string( $parents_list ) ) {
578 continue;
579 }
580
581 $return[] = array(
582 'id' => $term->term_id,
583 'value' => rtrim( $parents_list, ' >' ), // Trim the separator added at the end by WP.
584 'link' => $this->links->get_edit_term_link_html( $term, $post_type ),
585 );
586 }
587
588 wp_die( wp_json_encode( $return ) );
589 }
590
591 /**
592 * Updates the translations term ids when splitting a shared term
593 * Splits translations if these are shared terms too
594 *
595 * @since 1.7
596 *
597 * @param int $term_id ID of the formerly shared term.
598 * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
599 * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
600 * @param string $taxonomy Taxonomy name.
601 * @return void
602 */
603 public function split_shared_term( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
604 if ( ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
605 return;
606 }
607
608 // Avoid recursion
609 static $avoid_recursion = false;
610 if ( $avoid_recursion ) {
611 return;
612 }
613
614 $lang = $this->model->term->get_language( $term_id );
615 if ( empty( $lang ) ) {
616 return;
617 }
618
619 $avoid_recursion = true;
620 $translations = array();
621
622 foreach ( $this->model->term->get_translations( $term_id ) as $key => $tr_id ) {
623 if ( $lang->slug == $key ) {
624 $translations[ $key ] = $new_term_id;
625 }
626 else {
627 $tr_term = get_term( $tr_id, $taxonomy );
628
629 if ( ! $tr_term instanceof WP_Term ) {
630 continue;
631 }
632
633 $split_term_id = _split_shared_term( $tr_id, $tr_term->term_taxonomy_id );
634
635 if ( is_int( $split_term_id ) ) {
636 $translations[ $key ] = $split_term_id;
637 } else {
638 $translations[ $key ] = $tr_id;
639 }
640
641 // Hack translation ids sent by the form to avoid overwrite in PLL_Admin_Filters_Term::save_translations
642 if ( isset( $_POST['term_tr_lang'][ $key ] ) && $_POST['term_tr_lang'][ $key ] == $tr_id ) { // phpcs:ignore WordPress.Security.NonceVerification
643 $_POST['term_tr_lang'][ $key ] = $translations[ $key ];
644 }
645 }
646
647 $this->model->term->set_language( $translations[ $key ], $key );
648 }
649
650 $this->model->term->save_translations( $new_term_id, $translations );
651 $avoid_recursion = false;
652 }
653
654 /**
655 * Returns the language for subsequently inserted term in admin.
656 *
657 * @since 3.3
658 *
659 * @param PLL_Language|null $lang Term language object if found, null otherwise.
660 * @return PLL_Language|null Language object, null if none found.
661 */
662 public function get_inserted_term_language( $lang ) {
663 if ( $lang instanceof PLL_Language ) {
664 return $lang;
665 }
666
667 if ( ! empty( $_POST['term_lang_choice'] ) && is_string( $_POST['term_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
668 $lang_slug = sanitize_key( $_POST['term_lang_choice'] ); // phpcs:ignore WordPress.Security.NonceVerification
669 $lang = $this->model->get_language( $lang_slug );
670 return $lang instanceof PLL_Language ? $lang : null;
671 }
672
673 if ( ! empty( $_POST['inline_lang_choice'] ) && is_string( $_POST['inline_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
674 $lang_slug = sanitize_key( $_POST['inline_lang_choice'] ); // phpcs:ignore WordPress.Security.NonceVerification
675 $lang = $this->model->get_language( $lang_slug );
676 return $lang instanceof PLL_Language ? $lang : null;
677 }
678
679 // *Post* bulk edit, in case a new term is created
680 if ( isset( $_GET['bulk_edit'], $_GET['inline_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
681 // Bulk edit does not modify the language
682 if ( -1 === (int) $_GET['inline_lang_choice'] ) { // phpcs:ignore WordPress.Security.NonceVerification
683 $lang = $this->model->post->get_language( $this->post_id );
684 return $lang instanceof PLL_Language ? $lang : null;
685 } elseif ( is_string( $_GET['inline_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
686 $lang_slug = sanitize_key( $_GET['inline_lang_choice'] ); // phpcs:ignore WordPress.Security.NonceVerification
687 $lang = $this->model->get_language( $lang_slug );
688 return $lang instanceof PLL_Language ? $lang : null;
689 }
690 }
691
692 // Special cases for default categories as the select is disabled.
693 $default_term = get_option( 'default_category' );
694
695 if ( ! is_numeric( $default_term ) ) {
696 return null;
697 }
698
699 if ( ! empty( $_POST['tag_ID'] ) && in_array( (int) $default_term, $this->model->term->get_translations( (int) $_POST['tag_ID'] ), true ) ) { // phpcs:ignore WordPress.Security.NonceVerification
700 $lang = $this->model->term->get_language( (int) $_POST['tag_ID'] ); // phpcs:ignore WordPress.Security.NonceVerification
701 return $lang instanceof PLL_Language ? $lang : null;
702 }
703
704 if ( ! empty( $_POST['tax_ID'] ) && in_array( (int) $default_term, $this->model->term->get_translations( (int) $_POST['tax_ID'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
705 $lang = $this->model->term->get_language( (int) $_POST['tax_ID'] ); // phpcs:ignore WordPress.Security.NonceVerification
706 return $lang instanceof PLL_Language ? $lang : null;
707 }
708
709 return null;
710 }
711
712 /**
713 * Filters the subsequently inserted term parent in admin.
714 *
715 * @since 3.3
716 *
717 * @param int $parent Parent term ID, 0 if none found.
718 * @param string $taxonomy Term taxonomy.
719 * @return int Parent term ID if found, 0 otherwise.
720 */
721 public function get_inserted_term_parent( $parent, $taxonomy ) {
722 if ( $parent ) {
723 return $parent;
724 }
725
726 if ( isset( $_POST['parent'], $_POST['term_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
727 $parent = intval( $_POST['parent'] ); // phpcs:ignore WordPress.Security.NonceVerification
728 } elseif ( isset( $_POST[ "new{$taxonomy}_parent" ], $_POST['term_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
729 $parent = intval( $_POST[ "new{$taxonomy}_parent" ] ); // phpcs:ignore WordPress.Security.NonceVerification
730 }
731
732 return $parent;
733 }
734 }
735