PluginProbe
Polylang / 1.5.2
Polylang v1.5.2
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 / admin / admin-filters-term.php

admin-filters-term.php in Polylang 1.5.2, at admin/admin-filters-term.php

583 lines 19.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * manages filters and actions related to terms on admin side
5 *
6 * @since 1.2
7 */
8 class PLL_Admin_Filters_Term {
9 public $links, $model, $options, $curlang, $pref_lang;
10 protected $pre_term_name; // used to store the term name before creating a slug if needed
11
12 /*
13 * constructor: setups filters and actions
14 *
15 * @param object $polylang
16 */
17 public function __construct(&$polylang) {
18 $this->links = &$polylang->links;
19 $this->model = &$polylang->model;
20 $this->options = &$polylang->options;
21 $this->curlang = &$polylang->curlang;
22 $this->pref_lang = &$polylang->pref_lang;
23
24 foreach ($this->model->get_translated_taxonomies() as $tax) {
25 // adds the language field in the 'Categories' and 'Post Tags' panels
26 add_action($tax.'_add_form_fields', array(&$this, 'add_term_form'));
27
28 // adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels
29 add_action($tax.'_edit_form_fields', array(&$this, 'edit_term_form'));
30
31 // adds action related to languages when deleting categories and post tags
32 add_action('delete_'.$tax, array(&$this, 'delete_term'));
33 }
34
35 // adds actions related to languages when creating or saving categories and post tags
36 add_filter('wp_dropdown_cats', array(&$this, 'wp_dropdown_cats'));
37 add_filter('pre_insert_term', array(&$this, 'pre_insert_term'), 10, 2);
38 add_action('create_term', array(&$this, 'save_term'), 10, 3);
39 add_action('edit_term', array(&$this, 'save_term'), 10, 3);
40 add_filter('pre_term_name', array(&$this, 'pre_term_name'));
41 add_filter('pre_term_slug', array(&$this, 'pre_term_slug'), 10, 2);
42
43 // ajax response for edit term form
44 add_action('wp_ajax_term_lang_choice', array(&$this,'term_lang_choice'));
45 add_action('wp_ajax_pll_terms_not_translated', array(&$this,'ajax_terms_not_translated'));
46
47 // filters categories and post tags by language
48 add_filter('terms_clauses', array(&$this, 'terms_clauses'), 10, 3);
49
50 // backward compatibility WP < 3.7
51 version_compare($GLOBALS['wp_version'], '3.7', '<') ?
52 add_action('wp_ajax_polylang-ajax-tag-search', array(&$this,'ajax_tag_search')) :
53 add_action('wp_ajax_polylang-ajax-tag-search', 'wp_ajax_ajax_tag_search'); // take profit of new filter, cache...
54
55 add_filter('option_default_category', array(&$this, 'option_default_category'));
56 }
57
58 /*
59 * adds the language field in the 'Categories' and 'Post Tags' panels
60 *
61 * @since 0.1
62 */
63 public function add_term_form() {
64 $taxonomy = $_GET['taxonomy'];
65 $post_type = isset($GLOBALS['post_type']) ? $GLOBALS['post_type'] : $_REQUEST['post_type'];
66 $lang = isset($_GET['new_lang']) ? $this->model->get_language($_GET['new_lang']) : $this->pref_lang;
67 $dropdown = new PLL_Walker_Dropdown();
68
69 wp_nonce_field('pll_language', '_pll_nonce');
70
71 printf('
72 <div class="form-field">
73 <label for="term_lang_choice">%s</label>
74 %s
75 <p>%s</p>
76 </div>',
77 __('Language', 'polylang'),
78 $dropdown->walk($this->model->get_languages_list(), array('name' => 'term_lang_choice', 'value' => 'term_id', 'selected' => $lang ? $lang->term_id : '')),
79 __('Sets the language', 'polylang')
80 );
81
82 if (!empty($_GET['from_tag']))
83 printf('<input type="hidden" name="from_tag" value="%d" />', $_GET['from_tag']);
84
85 // adds translation fields
86 echo '<div id="term-translations" class="form-field">';
87 if ($lang)
88 include(PLL_ADMIN_INC.'/view-translations-term.php');
89 echo '</div>'."\n";
90 }
91
92 /*
93 * adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels
94 *
95 * @since 0.1
96 */
97 public function edit_term_form($tag) {
98 $term_id = $tag->term_id;
99 $lang = $this->model->get_term_language($term_id);
100 $taxonomy = $tag->taxonomy;
101 $post_type = isset($GLOBALS['post_type']) ? $GLOBALS['post_type'] : $_REQUEST['post_type'];
102 $dropdown = new PLL_Walker_Dropdown();
103
104 wp_nonce_field('pll_language', '_pll_nonce');
105
106 printf('
107 <tr class="form-field">
108 <th scope="row">
109 <label for="term_lang_choice">%s</label>
110 </th>
111 <td>
112 %s<br />
113 <span class="description">%s</span>
114 </td>
115 </tr>',
116 __('Language', 'polylang'),
117 $dropdown->walk($this->model->get_languages_list(), array('name' => 'term_lang_choice', 'value' => 'term_id', 'selected' => $lang ? $lang->term_id : '')),
118 __('Sets the language', 'polylang')
119 );
120
121 echo '<tr id="term-translations" class="form-field">';
122 if ($lang)
123 include(PLL_ADMIN_INC.'/view-translations-term.php');
124 echo '</tr>'."\n";
125 }
126
127 /*
128 * translates term parent if exists when using "Add new" (translation)
129 *
130 * @since 0.7
131 *
132 * @param string html markup for dropdown list of categories
133 * @return string modified html
134 */
135 public function wp_dropdown_cats($output) {
136 if (isset($_GET['taxonomy'], $_GET['from_tag'], $_GET['new_lang']) && $id = get_term($_GET['from_tag'], $_GET['taxonomy'])->parent) {
137 if ($parent = $this->model->get_translation('term', $id, $_GET['new_lang']))
138 return str_replace('"'.$parent.'"', '"'.$parent.'" selected="selected"', $output);
139 }
140 return $output;
141 }
142
143 /*
144 * prevents duplicating a term translation
145 *
146 * @since 1.3
147 *
148 * @param string $term The term to add or update.
149 * @param string $taxonomy The taxonomy to which to add the term
150 *
151 * @return object|string WP_Error object if the translation already exits, unmodified $term otherwise
152 */
153 public function pre_insert_term($term, $taxonomy) {
154 if (isset($_POST['action'], $_POST['from_tag'], $_POST['term_lang_choice']) && 'add-tag' == $_POST['action'] && $this->model->get_translation('term', $_POST['from_tag'], $_POST['term_lang_choice'])) {
155 $from_term = get_term($_POST['from_tag'], $taxonomy);
156 return new WP_Error('term_translation_exists', sprintf(
157 __('A translation does already exist for %s', 'polylang'),
158 $from_term->name
159 ));
160 }
161 return $term;
162 }
163
164 /*
165 * saves language
166 *
167 * @since 1.5
168 *
169 * @param int $term_id
170 * @param string $taxonomy
171 */
172 protected function save_language($term_id, $taxonomy) {
173 // security checks are necessary to accept language modifications
174 // as 'wp_update_term' can be called from outside WP admin
175
176 // edit tags
177 if (isset($_POST['term_lang_choice'])) {
178 if ('add-' . $taxonomy == $_POST['action'])
179 check_ajax_referer($_POST['action'], '_ajax_nonce-add-' . $taxonomy); // category metabox
180
181 else
182 check_admin_referer('pll_language', '_pll_nonce'); // edit tags or tags metabox
183
184 $this->model->set_term_language($term_id, $_POST['term_lang_choice']);
185 }
186
187 // quick edit
188 elseif (isset($_POST['inline_lang_choice'])) {
189 check_ajax_referer('taxinlineeditnonce', '_inline_edit');
190
191 if (isset($_POST['inline-save-tax']) && $this->model->get_term_language($term_id)->slug != $_POST['inline_lang_choice'])
192 $this->model->delete_translation('term', $term_id);
193
194 $this->model->set_term_language($term_id, $_POST['inline_lang_choice']);
195 }
196
197 // edit post
198 elseif (isset($_POST['post_lang_choice'])) {// FIXME should be useless now
199 check_admin_referer('pll_language', '_pll_nonce');
200 $this->model->set_term_language($term_id, $_POST['post_lang_choice']);
201 }
202
203 elseif ($this->model->get_term_language($term_id))
204 {} // avoids breaking the language if the term is updated outside the edit post or edit tag pages
205
206 // sets language from term parent if exists thanks to Scott Kingsley Clark
207 elseif (($term = get_term($term_id, $taxonomy)) && !empty($term->parent) && $parent_lang = $this->model->get_term_language($term->parent))
208 $this->model->set_term_language($term_id, $parent_lang);
209
210 else
211 $this->model->set_term_language($term_id, $this->pref_lang);
212 }
213
214 /*
215 * save translations from our form
216 *
217 * @since 1.5
218 *
219 * @param int $term_id
220 * @return array
221 */
222 protected function save_translations($term_id) {
223 // security check
224 // as 'wp_update_term' can be called from outside WP admin
225 check_admin_referer('pll_language', '_pll_nonce');
226
227 // save translations after checking the translated term is in the right language (as well as cast id to int)
228 foreach ($_POST['term_tr_lang'] as $lang => $tr_id) {
229 $tr_lang = $this->model->get_term_language((int) $tr_id);
230 $translations[$lang] = $tr_lang && $tr_lang->slug == $lang ? (int) $tr_id : 0;
231 }
232
233 $this->model->save_translations('term', $term_id, $translations);
234
235 return $translations;
236 }
237
238 /*
239 * called when a category or post tag is created or edited
240 * saves language and translations
241 *
242 * @since 0.1
243 *
244 * @param int $term_id
245 * @param int $tt_id term taxononomy id
246 * @param string $taxonomy
247 */
248 public function save_term($term_id, $tt_id, $taxonomy) {
249 // does nothing except on taxonomies which are filterable
250 if (!$this->model->is_translated_taxonomy($taxonomy))
251 return;
252
253 // capability check
254 // as 'wp_update_term' can be called from outside WP admin
255 $tax = get_taxonomy($taxonomy);
256 if (!current_user_can($tax->cap->edit_terms))
257 wp_die( __( 'Cheatin&#8217; uh?' ) );
258
259 $this->save_language($term_id, $taxonomy);
260
261 if (isset($_POST['term_tr_lang']))
262 $translations = $this->save_translations($term_id);
263
264 do_action('pll_save_term', $term_id, $taxonomy, empty($translations) ? $this->model->get_translations('term', $term_id) : $translations);
265 }
266
267 /*
268 * stores the term name for use in pre_term_slug
269 *
270 * @since 0.9.5
271 *
272 * @param string $name term name
273 * @return string unmodified term name
274 */
275 public function pre_term_name($name) {
276 return $this->pre_term_name = $name;
277 }
278
279 /*
280 * creates the term slug in case the term already exists in another language
281 *
282 * @since 0.9.5
283 *
284 * @param string $slug
285 * @param string $taxonomy
286 * @return string
287 */
288 public function pre_term_slug($slug, $taxonomy) {
289 $name = sanitize_title($this->pre_term_name);
290
291 // if the new term has the same name as a language, we *need* to differentiate the term
292 // see http://core.trac.wordpress.org/ticket/23199
293 if (term_exists($name, 'language') && !term_exists($name, $taxonomy) && (!$slug || $slug == $name))
294 $slug = $name . '-' . $taxonomy; // a convenient slug which may be modified later by the user
295
296 // if the term already exists in another language
297 if (!$slug && $this->model->is_translated_taxonomy($taxonomy) && term_exists($name, $taxonomy)) {
298 if (isset($_POST['term_lang_choice']))
299 $slug = $name . '-' . $this->model->get_language($_POST['term_lang_choice'])->slug;
300
301 elseif (isset($_POST['inline_lang_choice']))
302 $slug = $name . '-' . $this->model->get_language($_POST['inline_lang_choice'])->slug;
303 }
304
305 return $slug;
306 }
307
308 /*
309 * called when a category or post tag is deleted
310 * deletes language and translations
311 *
312 * @since 0.1
313 *
314 * @param int $term_id
315 */
316 public function delete_term($term_id) {
317 $this->model->delete_translation('term', $term_id);
318 $this->model->delete_term_language($term_id);
319 }
320
321 /*
322 * ajax response for edit term form
323 *
324 * @since 0.2
325 */
326 public function term_lang_choice() {
327 check_ajax_referer('pll_language', '_pll_nonce');
328
329 $lang = $this->model->get_language($_POST['lang']);
330 $term_id = isset($_POST['term_id']) ? $_POST['term_id'] : null;
331 $taxonomy = $_POST['taxonomy'];
332 $post_type = $_POST['post_type'];
333
334 ob_start();
335 if ($lang)
336 include(PLL_ADMIN_INC.'/view-translations-term.php');
337 $x = new WP_Ajax_Response(array('what' => 'translations', 'data' => ob_get_contents()));
338 ob_end_clean();
339
340 // parent dropdown list (only for hierarchical taxonomies)
341 // $args copied from edit_tags.php except echo
342 if (is_taxonomy_hierarchical($taxonomy)) {
343 $args = array(
344 'hide_empty' => 0,
345 'hide_if_empty' => false,
346 'taxonomy' => $taxonomy,
347 'name' => 'parent',
348 'orderby' => 'name',
349 'hierarchical' => true,
350 'show_option_none' => __('None'),
351 'echo' => 0,
352 );
353 $x->Add(array('what' => 'parent', 'data' => wp_dropdown_categories($args)));
354 }
355
356 // tag cloud
357 // tests copied from edit_tags.php
358 else {
359 $tax = get_taxonomy($taxonomy);
360 if (!is_null($tax->labels->popular_items)) {
361 $args = array('taxonomy' => $taxonomy, 'echo' => false);
362 if (current_user_can($tax->cap->edit_terms))
363 $args = array_merge($args, array('link' => 'edit'));
364
365 if ($tag_cloud = wp_tag_cloud($args))
366 $x->Add(array('what' => 'tag_cloud', 'data' => '<h3>'.$tax->labels->popular_items.'</h3>'.$tag_cloud));
367 }
368 }
369
370 $x->send();
371 }
372
373 /*
374 * ajax response for input in translation autocomplete input box
375 *
376 * @since 1.5
377 */
378 public function ajax_terms_not_translated() {
379 check_ajax_referer('pll_language', '_pll_nonce');
380
381 $return = array();
382
383 // it is more efficient to use one common query for all languages as soon as there are more than 2
384 // pll_get_terms_not_translated arg to identify this query in terms_clauses filter
385 foreach (get_terms($_REQUEST['taxonomy'], 'hide_empty=0&pll_get_terms_not_translated=1&name__like=' . $_REQUEST['term']) as $term) {
386 $lang = $this->model->get_term_language($term->term_id);
387
388 if ($lang && $lang->slug == $_REQUEST['translation_language'] && !$this->model->get_translation('term', $term->term_id, $_REQUEST['term_language']))
389 $return[] = array(
390 'id' => $term->term_id,
391 'value' => $term->name,
392 'link' => $this->edit_translation_link($term->term_id, $_REQUEST['taxonomy'], $_REQUEST['post_type'])
393 );
394 }
395
396 // add current translation in list
397 // not in add term for as term_id is not set
398 if ('undefined' !== $_REQUEST['term_id'] && $term_id = $this->model->get_translation('term', $_REQUEST['term_id'], $_REQUEST['translation_language'])) {
399 $term = get_term($term_id, $_REQUEST['taxonomy']);
400 array_unshift($return, array(
401 'id' => $term_id,
402 'value' => $term->name,
403 'link' => $this->edit_translation_link($term->term_id, $_REQUEST['taxonomy'], $_REQUEST['post_type'])
404 ));
405 }
406
407 wp_die(json_encode($return));
408 }
409
410 /*
411 * filters categories and post tags by language when needed on admin side
412 *
413 * @since 0.5
414 *
415 * @param array $clauses list of sql clauses
416 * @param array $taxonomies list of taxonomies
417 * @param array $args get_terms arguments
418 * @return array modified sql clauses
419 */
420 public function terms_clauses($clauses, $taxonomies, $args) {
421 // does nothing except on taxonomies which are filterable
422 foreach ($taxonomies as $tax)
423 if (!$this->model->is_translated_taxonomy($tax))
424 return $clauses;
425
426 if (function_exists('get_current_screen'))
427 $screen = get_current_screen(); // since WP 3.1, may not be available the first time(s) get_terms is called
428
429 // don't filter nav menus on nav menus screen
430 if (isset($screen) && 'nav-menus' == $screen->base && in_array('nav_menu', $taxonomies))
431 return $clauses;
432
433 // if get_terms is queried with a 'lang' parameter
434 if (!empty($args['lang']))
435 return $this->model->terms_clauses($clauses, $args['lang']);
436
437 // does nothing in Languages and dasboard admin panels
438 if (isset($screen) && in_array($screen->base, array('toplevel_page_mlang', 'dashboard')))
439 return $clauses;
440
441 // do not filter 'get_terms_not_translated'
442 if (!empty($args['pll_get_terms_not_translated']))
443 return $clauses;
444
445 // The only ajax response I want to deal with is when changing the language in post metabox
446 if (isset($_POST['action']) && !in_array($_POST['action'], array('post_lang_choice', 'term_lang_choice', 'get-tagcloud')))
447 return $clauses;
448
449 // I only want to filter the parent dropdown list when editing a term in a hierarchical taxonomy
450 if (isset($_POST['action']) && $_POST['action'] == 'term_lang_choice' && !(isset($args['class']) || isset($args['unit'])))
451 return $clauses;
452
453 // ajax response for changing the language in the post metabox (or in the edit-tags panels)
454 if (isset($_POST['lang']))
455 $lang = $this->model->get_language($_POST['lang']);
456
457 // ajax tag search since WP 3.7
458 elseif (!empty($_GET['lang']) && isset($_GET['action']) && 'polylang-ajax-tag-search' == $_GET['action'])
459 $lang = $this->model->get_language($_GET['lang']);
460
461 // the post (or term) is created with the 'add new' (translation) link
462 // test of $args['page'] to avoid filtering the terms list table in edit-tags panel
463 elseif (!empty($_GET['new_lang']) && empty($args['page']))
464 $lang = $this->model->get_language($_GET['new_lang']);
465
466 // FIXME can we simplify how we deal with the admin language filter?
467 // the language filter selection has just changed
468 // test $screen->base to avoid interference between the language filter and the post language selection and the category parent dropdown list
469 elseif (!empty($_GET['lang']) && !(isset($screen) && in_array($screen->base, array('post', 'edit-tags')))) {
470 if ($_GET['lang'] != 'all')
471 $lang = $this->model->get_language($_GET['lang']);
472 elseif ($screen->base == 'edit-tags' && isset($args['class']))
473 $lang = $this->pref_lang; // parent dropdown
474 }
475
476 // again the language filter
477 elseif (!empty($this->curlang) && (isset($screen) && $screen->base != 'post' && !($screen->base == 'edit-tags' && isset($args['class'])))) // don't apply to post edit and the category parent dropdown list
478 $lang = $this->curlang;
479
480 elseif (isset($_GET['post']))
481 $lang = $this->model->get_post_language($_GET['post']);
482
483 // for the parent dropdown list in edit term
484 elseif (isset($_GET['tag_ID']))
485 $lang = $this->model->get_term_language($_GET['tag_ID']);
486
487 // when a new category is created in the edit post panel
488 elseif (isset($_POST['term_lang_choice']))
489 $lang = $this->model->get_language($_POST['term_lang_choice']);
490
491 // for a new post (or the parent dropdown list of a new term)
492 elseif (isset($screen) && ($screen->base == 'post' || ($screen->base == 'edit-tags' && isset($args['class']))))
493 $lang = $this->pref_lang;
494
495 // adds our clauses to filter by current language
496 return !empty($lang) ? $this->model->terms_clauses($clauses, $lang) : $clauses;
497 }
498
499 /*
500 * replaces ajax tag search of WP to filter tags by language
501 * backward compatibility WP < 3.7
502 * see http://core.trac.wordpress.org/ticket/25231
503 *
504 * @since 0.7
505 */
506 public function ajax_tag_search() {
507 global $wpdb;
508
509 if ( isset( $_GET['tax'] ) ) {
510 $taxonomy = sanitize_key( $_GET['tax'] );
511 $tax = get_taxonomy( $taxonomy );
512 if ( ! $tax )
513 die( '0' );
514 if ( ! current_user_can( $tax->cap->assign_terms ) )
515 die( '-1' );
516 } else {
517 die('0');
518 }
519
520 $s = stripslashes( $_GET['q'] );
521
522 if ( false !== strpos( $s, ',' ) ) {
523 $s = explode( ',', $s );
524 $s = $s[count( $s ) - 1];
525 }
526 $s = trim( $s );
527 if ( strlen( $s ) < 2 )
528 die; // require 2 chars for matching
529
530 $lang = $this->model->get_language($_GET['lang']);
531
532 $results = $wpdb->get_col( $wpdb->prepare(
533 "SELECT t.name FROM $wpdb->term_taxonomy AS tt
534 INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id
535 INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = t.term_id
536 WHERE tt.taxonomy = %s AND t.name LIKE (%s) AND pll_tr.term_taxonomy_id = %d",
537 $taxonomy, '%' . like_escape( $s ) . '%', $lang->tl_term_taxonomy_id ) );
538
539 echo join( $results, "\n" );
540 die;
541 }
542
543 /*
544 * hack to avoid displaying delete link for the default category in all languages
545 * also returns the default category in the right language when called from wp_delete_term
546 *
547 * @since 1.2
548 *
549 * @param int $value
550 * @return int
551 */
552 public function option_default_category($value) {
553 $traces = debug_backtrace();
554
555 if (isset($traces[4])) {
556 if (in_array($traces[4]['function'], array('column_cb', 'column_name')) && in_array($traces[4]['args'][0]->term_id, $this->model->get_translations('term', $value)))
557 return $traces[4]['args'][0]->term_id;
558
559 if ('wp_delete_term' == $traces[4]['function'])
560 return $this->model->get_term($value, $this->model->get_term_language($traces[4]['args'][0]));
561 }
562 return $value;
563 }
564
565 /*
566 * returns html markup for a translation link
567 *
568 * @since 1.4
569 *
570 * @param object $term_id translation term id
571 * @param string $taxonomy
572 * @param string $post_type
573 * @return string
574 */
575 public function edit_translation_link($term_id, $taxonomy, $post_type) {
576 return sprintf(
577 '<a href="%1$s" class="pll_icon_edit title="%2$s"></a></td>',
578 esc_url(get_edit_term_link($term_id, $taxonomy, $post_type)),
579 __('Edit','polylang')
580 );
581 }
582 }
583