PluginProbe
Polylang / 1.8.2
Polylang v1.8.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-post.php

admin-filters-post.php in Polylang 1.8.2, at admin/admin-filters-post.php

513 lines 17.7 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 posts on admin side
5 *
6 * @since 1.2
7 */
8 class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
9 public $options, $curlang;
10
11 /*
12 * constructor: setups filters and actions
13 *
14 * @since 1.2
15 *
16 * @param object $polylang
17 */
18 public function __construct( &$polylang ) {
19 parent::__construct( $polylang );
20 $this->options = &$polylang->options;
21 $this->curlang = &$polylang->curlang;
22
23 add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueue_scripts' ) );
24
25 // filters posts, pages and media by language
26 add_action( 'parse_query', array( &$this, 'parse_query' ) );
27
28 // adds the Languages box in the 'Edit Post' and 'Edit Page' panels
29 add_action( 'add_meta_boxes', array( &$this, 'add_meta_boxes' ) );
30
31 // ajax response for changing the language in the post metabox
32 add_action( 'wp_ajax_post_lang_choice', array( &$this, 'post_lang_choice' ) );
33 add_action( 'wp_ajax_pll_posts_not_translated', array( &$this, 'ajax_posts_not_translated' ) );
34
35 // adds actions and filters related to languages when creating, saving or deleting posts and pages
36 add_action( 'save_post', array( &$this, 'save_post' ), 21, 3 ); // priority 21 to come after advanced custom fields ( 20 ) and before the event calendar which breaks everything after 25
37 add_filter( 'wp_insert_post_parent', array( &$this, 'wp_insert_post_parent' ), 10, 4 );
38 add_action( 'before_delete_post', array( &$this, 'delete_post' ) );
39 if ( $this->options['media_support'] ) {
40 add_action( 'delete_attachment', array( &$this, 'delete_post' ) ); // action shared with media
41 }
42
43 // filters the pages by language in the parent dropdown list in the page attributes metabox
44 add_filter( 'page_attributes_dropdown_pages_args', array( &$this, 'page_attributes_dropdown_pages_args' ), 10, 2 );
45 }
46
47 /*
48 * outputs a javascript list of terms ordered by language and hierarchical taxonomies
49 * to filter the category checklist per post language in quick edit
50 * outputs a javascript list of pages ordered by language
51 * to filter the parent dropdown per post language in quick edit
52 *
53 * @since 1.7
54 */
55 public function admin_enqueue_scripts() {
56 $screen = get_current_screen();
57
58 //hierarchical taxonomies
59 if ( 'edit' == $screen->base && $taxonomies = get_object_taxonomies( $screen->post_type, 'object' ) ) {
60 // get translated hierarchical taxonomies
61 foreach ( $taxonomies as $taxonomy ) {
62 if ( $taxonomy->hierarchical && $this->model->is_translated_taxonomy( $taxonomy->name ) ) {
63 $hierarchical_taxonomies[] = $taxonomy->name;
64 }
65 }
66
67 if ( ! empty( $hierarchical_taxonomies ) ) {
68 $terms = get_terms( $hierarchical_taxonomies, array( 'get' => 'all' ) );
69
70 foreach ( $terms as $term ) {
71 if ( $lang = $this->model->term->get_language( $term->term_id ) ) {
72 $term_languages[ $lang->slug ][ $term->taxonomy ][] = $term->term_id;
73 }
74 }
75
76 // send all these data to javascript
77 if ( ! empty( $term_languages ) ) {
78 wp_localize_script( 'pll_post', 'pll_term_languages', $term_languages );
79 }
80 }
81 }
82
83 // hierarchical post types
84 if ( 'edit' == $screen->base && is_post_type_hierarchical( $screen->post_type ) ) {
85 $pages = get_pages();
86
87 foreach ( $pages as $page ) {
88 if ( $lang = $this->model->post->get_language( $page->ID ) ) {
89 $page_languages[ $lang->slug ][] = $page->ID;
90 }
91 }
92
93 // send all these data to javascript
94 if ( ! empty( $page_languages ) ) {
95 wp_localize_script( 'pll_post', 'pll_page_languages', $page_languages );
96 }
97 }
98 }
99
100 /*
101 * filters posts, pages and media by language
102 *
103 * @since 0.1
104 *
105 * @param object $query a WP_Query object
106 */
107 public function parse_query( $query ) {
108 $qvars = &$query->query_vars;
109
110 // do not filter post types such as nav_menu_item
111 if ( isset( $qvars['post_type'] ) && ! $this->model->is_translated_post_type( $qvars['post_type'] ) ) {
112 unset( $qvars['lang'] );
113 return;
114 }
115
116 if ( isset( $qvars['post_type'] ) && ! isset( $qvars['lang'] ) ) {
117 // filters the list of media ( or wp-links ) by language when uploading from post
118 if ( isset( $_REQUEST['pll_post_id'] ) && $lang = $this->model->post->get_language( (int) $_REQUEST['pll_post_id'] ) ) {
119 $query->set( 'lang', $lang->slug );
120 } elseif ( ! empty( $this->curlang ) ) {
121 $qvars['lang'] = $this->curlang->slug;
122 }
123 }
124
125 if ( isset( $qvars['lang'] ) && 'all' === $qvars['lang'] ) {
126 unset( $qvars['lang'] );
127 }
128 }
129
130 /*
131 * adds the Language box in the 'Edit Post' and 'Edit Page' panels ( as well as in custom post types panels )
132 *
133 * @since 0.1
134 *
135 * @param string $post_type
136 */
137 public function add_meta_boxes( $post_type ) {
138 if ( $this->model->is_translated_post_type( $post_type ) ) {
139 add_meta_box( 'ml_box', __( 'Languages','polylang' ), array( &$this, 'post_language' ), $post_type, 'side', 'high' );
140 }
141 }
142
143 /*
144 * displays the Languages metabox in the 'Edit Post' and 'Edit Page' panels
145 *
146 * @since 0.1
147 */
148 public function post_language() {
149 global $post_ID;
150 $post_id = $post_ID;
151 $post_type = get_post_type( $post_ID );
152
153 $lang = ( $lg = $this->model->post->get_language( $post_ID ) ) ? $lg :
154 ( isset( $_GET['new_lang'] ) ? $this->model->get_language( $_GET['new_lang'] ) :
155 $this->pref_lang );
156
157 $dropdown = new PLL_Walker_Dropdown();
158
159 wp_nonce_field( 'pll_language', '_pll_nonce' );
160
161 // NOTE: the class "tags-input" allows to include the field in the autosave $_POST ( see autosave.js )
162 printf( '
163 <p><strong>%1$s</strong></p>
164 <label class="screen-reader-text" for="%2$s">%1$s</label>
165 <div id="select-%3$s-language">%4$s</div>
166 <div id="post-translations" class="translations">',
167 __( 'Language', 'polylang' ),
168 $id = ( 'attachment' === $post_type ) ? sprintf( 'attachments[%d][language]', $post_ID ) : 'post_lang_choice',
169 'attachment' === $post_type ? 'media' : 'post',
170 $dropdown->walk( $this->model->get_languages_list(), array(
171 'name' => $id,
172 'class' => 'post_lang_choice tags-input',
173 'selected' => $lang ? $lang->slug : '',
174 'flag' => true
175 ) )
176 );
177
178 if ( $lang ) {
179 include( PLL_ADMIN_INC . '/view-translations-' . ( 'attachment' == $post_type ? 'media' : 'post' ) . '.php' );
180 }
181 echo '</div>' . "\n";
182 }
183
184 /*
185 * ajax response for changing the language in the post metabox
186 *
187 * @since 0.2
188 */
189 public function post_lang_choice() {
190 check_ajax_referer( 'pll_language', '_pll_nonce' );
191
192 global $post_ID; // obliged to use the global variable for wp_popular_terms_checklist
193 $post_id = $post_ID = (int) $_POST['post_id'];
194 $lang = $this->model->get_language( $_POST['lang'] );
195
196 $post_type = $_POST['post_type'];
197 $post_type_object = get_post_type_object( $post_type );
198 if ( ! current_user_can( $post_type_object->cap->edit_post, $post_ID ) ) {
199 wp_die( -1 );
200 }
201
202 $this->model->post->set_language( $post_ID, $lang ); // save language, useful to set the language when uploading media from post
203
204 ob_start();
205 if ( $lang ) {
206 include( PLL_ADMIN_INC . '/view-translations-' . ( 'attachment' == $post_type ? 'media' : 'post' ) . '.php' );
207 }
208 $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
209 ob_end_clean();
210
211 // categories
212 if ( isset( $_POST['taxonomies'] ) ) {
213 // not set for pages
214 foreach ( $_POST['taxonomies'] as $taxname ) {
215 $taxonomy = get_taxonomy( $taxname );
216
217 ob_start();
218 $popular_ids = wp_popular_terms_checklist( $taxonomy->name );
219 $supplemental['populars'] = ob_get_contents();
220 ob_end_clean();
221
222 ob_start();
223 // use $post_ID to remember ckecked terms in case we come back to the original language
224 wp_terms_checklist( $post_ID, array( 'taxonomy' => $taxonomy->name, 'popular_cats' => $popular_ids ) );
225 $supplemental['all'] = ob_get_contents();
226 ob_end_clean();
227
228 $supplemental['dropdown'] = wp_dropdown_categories( array(
229 'taxonomy' => $taxonomy->name,
230 'hide_empty' => 0,
231 'name' => 'new'.$taxonomy->name.'_parent',
232 'orderby' => 'name',
233 'hierarchical' => 1,
234 'show_option_none' => '&mdash; '.$taxonomy->labels->parent_item.' &mdash;',
235 'echo' => 0,
236 ) );
237
238 $x->Add( array( 'what' => 'taxonomy', 'data' => $taxonomy->name, 'supplemental' => $supplemental ) );
239 }
240 }
241
242 // parent dropdown list ( only for hierarchical post types )
243 if ( in_array( $post_type, get_post_types( array( 'hierarchical' => true ) ) ) ) {
244 $post = get_post( $post_ID );
245
246 // args and filter from 'page_attributes_meta_box' in wp-admin/includes/meta-boxes.php of WP 4.2.1
247 $dropdown_args = array(
248 'post_type' => $post->post_type,
249 'exclude_tree' => $post->ID,
250 'selected' => $post->post_parent,
251 'name' => 'parent_id',
252 'show_option_none' => __( '(no parent)' ),
253 'sort_column' => 'menu_order, post_title',
254 'echo' => 0,
255 );
256 $dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); // since WP 3.3
257
258 $x->Add( array( 'what' => 'pages', 'data' => wp_dropdown_pages( $dropdown_args ) ) );
259 }
260
261 // flag
262 $x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
263
264 $x->send();
265 }
266
267 /*
268 * ajax response for input in translation autocomplete input box
269 *
270 * @since 1.5
271 */
272 public function ajax_posts_not_translated() {
273 check_ajax_referer( 'pll_language', '_pll_nonce' );
274
275 if ( ! post_type_exists( $_GET['post_type'] ) ) {
276 die( 0 );
277 }
278
279 $post_language = $this->model->get_language( $_GET['post_language'] );
280 $translation_language = $this->model->get_language( $_GET['translation_language'] );
281
282 // don't order by title: see https://wordpress.org/support/topic/find-translated-post-when-10-is-not-enough
283 $args = array(
284 's' => wp_unslash( $_GET['term'] ),
285 'suppress_filters' => 0, // to make the post_fields filter work
286 'lang' => 0, // avoid admin language filter
287 'numberposts' => 20, // limit to 20 posts
288 'post_status' => 'any',
289 'post_type' => $_GET['post_type'],
290 'tax_query' => array( array(
291 'taxonomy' => 'language',
292 'field' => 'term_taxonomy_id', // WP 3.5+
293 'terms' => $translation_language->term_taxonomy_id,
294 ) )
295 );
296
297 // allow plugins to change args help fixing edge cases: see same topic as above
298 $args = apply_filters( 'pll_ajax_posts_not_translated_args', $args );
299 $posts = get_posts( $args );
300
301 $return = array();
302
303 foreach ( $posts as $key => $post ) {
304 if ( ! $this->model->post->get_translation( $post->ID, $post_language ) ) {
305 $return[] = array(
306 'id' => $post->ID,
307 'value' => $post->post_title,
308 'link' => $this->links->edit_post_translation_link( $post->ID ),
309 );
310 }
311 }
312
313 // add current translation in list
314 if ( $post_id = $this->model->post->get_translation( (int) $_GET['pll_post_id'], $translation_language ) ) {
315 $post = get_post( $post_id );
316 array_unshift( $return, array(
317 'id' => $post_id,
318 'value' => $post->post_title,
319 'link' => $this->links->edit_post_translation_link( $post_id ),
320 ) );
321 }
322
323 wp_die( json_encode( $return ) );
324 }
325
326 /*
327 * saves language
328 * checks the terms saved are in the right language
329 *
330 * @since 1.5
331 *
332 * @param int $post_id
333 * @param array $post
334 */
335 protected function save_language( $post_id, $post ) {
336 // security checks are necessary to accept language modifications
337 // as 'wp_insert_post' can be called from outside WP admin
338
339 // edit post
340 if ( isset( $_POST['post_lang_choice'] ) ) {
341 check_admin_referer( 'pll_language', '_pll_nonce' );
342 $this->model->post->set_language( $post_id, $lang = $this->model->get_language( $_POST['post_lang_choice'] ) );
343 }
344
345 // quick edit and bulk edit
346 // POST for quick edit, GET for bulk edit
347 elseif ( isset( $_REQUEST['inline_lang_choice'] ) ) {
348 // bulk edit does not modify the language
349 if ( isset( $_GET['bulk_edit'] ) && -1 == $_REQUEST['inline_lang_choice'] ) {
350 check_admin_referer( 'bulk-posts' );
351 $lang = $this->model->post->get_language( $post_id ); // get the post language for later use when saving terms
352 }
353 // a language is set in the language dropdown
354 else {
355 isset( $_GET['bulk_edit'] ) ? check_admin_referer( 'bulk-posts' ) : check_admin_referer( 'inlineeditnonce', '_inline_edit' );
356
357 $old_lang = $this->model->post->get_language( $post_id ); // stores the old language
358 $this->model->post->set_language( $post_id, $lang = $this->model->get_language( $_REQUEST['inline_lang_choice'] ) ); // set new language
359
360 // checks if the new language already exists in the translation group
361 if ( $old_lang && $old_lang->slug != $lang->slug ) {
362 $translations = $this->model->post->get_translations( $post_id );
363
364 // if yes, separate this post from the translation group
365 if ( array_key_exists( $lang->slug, $translations ) ) {
366 $this->model->post->delete_translation( $post_id );
367 }
368
369 elseif ( array_key_exists( $old_lang->slug, $translations ) ) {
370 unset( $translations[ $old_lang->slug ] );
371 $this->model->post->save_translations( $post_id, $translations );
372 }
373 }
374 }
375 }
376
377 // quick press
378 // 'post-quickpress-save', 'post-quickpress-publish' = backward compatibility WP < 3.8
379 elseif ( isset( $_REQUEST['action'] ) && in_array( $_REQUEST['action'], array( 'post-quickpress-save', 'post-quickpress-publish', 'post-quickdraft-save' ) ) ) {
380 check_admin_referer( 'add-' . $post->post_type );
381 $this->model->post->set_language( $post_id, $lang = $this->pref_lang ); // default language for Quick draft
382 }
383
384 else {
385 $this->set_default_language( $post_id );
386 }
387
388 // make sure we get save terms in the right language (especially tags with same name in different languages)
389 if ( ! empty( $lang ) ) {
390 // FIXME quite a lot of queries in foreach
391 foreach ( $this->model->get_translated_taxonomies() as $tax ) {
392 $terms = get_the_terms( $post_id, $tax );
393
394 if ( is_array( $terms ) ) {
395 $newterms = array();
396 foreach ( $terms as $term ) {
397 // check if the term is in the correct language or if a translation exist ( mainly for default category )
398 if ( $newterm = $this->model->term->get( $term->term_id, $lang ) ) {
399 $newterms[] = (int) $newterm;
400 }
401
402 // or choose the correct language for tags ( initially defined by name )
403 elseif ( $newterm = $this->model->term_exists( $term->name, $tax, $term->parent, $lang ) ) {
404 $newterms[] = (int) $newterm; // cast is important otherwise we get 'numeric' tags
405 }
406
407 // or create the term in the correct language
408 elseif ( ! is_wp_error( $term_info = wp_insert_term( $term->name, $tax ) ) ) {
409 $newterms[] = (int) $term_info['term_id'];
410 }
411 }
412
413 wp_set_object_terms( $post_id, $newterms, $tax );
414 }
415 }
416 }
417 }
418
419 /*
420 * called when a post ( or page ) is saved, published or updated
421 * saves languages and translations
422 *
423 * @since 0.1
424 *
425 * @param int $post_id
426 * @param object $post
427 * @param bool $update whether it is an update or not
428 */
429 public function save_post( $post_id, $post, $update ) {
430 // does nothing except on post types which are filterable
431 if ( ! $this->model->is_translated_post_type( $post->post_type ) ) {
432 return;
433 }
434
435 if ( $id = wp_is_post_revision( $post_id ) ) {
436 $post_id = $id;
437 }
438
439 // capability check
440 // as 'wp_insert_post' can be called from outside WP admin
441 $post_type_object = get_post_type_object( $post->post_type );
442 if ( ( $update && current_user_can( $post_type_object->cap->edit_post, $post_id ) ) || ( ! $update && current_user_can( $post_type_object->cap->create_posts ) ) ) {
443 $this->save_language( $post_id, $post );
444
445 if ( isset( $_POST['post_tr_lang'] ) ) {
446 $translations = $this->save_translations( $post_id, $_POST['post_tr_lang'] );
447 }
448
449 do_action( 'pll_save_post', $post_id, $post, empty( $translations ) ? $this->model->post->get_translations( $post_id ) : $translations );
450 }
451
452 // attempts to set a default language even if no capability
453 else {
454 $this->set_default_language( $post_id );
455 }
456 }
457
458 /*
459 * make sure that the post parent is in the correct language when using bulk edit
460 *
461 * @since 1.8
462 *
463 * @param int $post_parent Post parent ID.
464 * @param int $post_id Post ID.
465 * @param array $new_postarr Array of parsed post data.
466 * @param array $postarr Array of sanitized, but otherwise unmodified post data.
467 * @return int
468 */
469 public function wp_insert_post_parent( $post_parent, $post_id, $new_postarr, $postarr ) {
470 if ( isset( $postarr['bulk_edit'] ) ) {
471 check_admin_referer( 'bulk-posts' );
472 $lang = -1 == $postarr['inline_lang_choice'] ?
473 $this->model->post->get_language( $post_id ) :
474 $this->model->get_language( $postarr['inline_lang_choice'] );
475 $post_parent = $this->model->post->get_translation( $post_parent, $lang );
476 }
477 return $post_parent;
478 }
479
480 /*
481 * called when a post, page or media is deleted
482 * don't delete translations if this is a post revision thanks to AndyDeGroo who catched this bug
483 * http://wordpress.org/support/topic/plugin-polylang-quick-edit-still-breaks-translation-linking-of-pages-in-072
484 *
485 * @since 0.1
486 *
487 * @param int $post_id
488 */
489 public function delete_post( $post_id ) {
490 if ( ! wp_is_post_revision( $post_id ) ) {
491 $this->model->post->delete_translation( $post_id );
492 }
493 }
494
495 /*
496 * filters the pages by language in the parent dropdown list in the page attributes metabox
497 *
498 * @since 0.6
499 *
500 * @param array $dropdown_args arguments passed to wp_dropdown_pages
501 * @param object $post
502 * @return array modified arguments
503 */
504 public function page_attributes_dropdown_pages_args( $dropdown_args, $post ) {
505 $dropdown_args['lang'] = isset( $_POST['lang'] ) ? $this->model->get_language( $_POST['lang'] ) : $this->model->post->get_language( $post->ID ); // ajax or not ?
506 if ( ! $dropdown_args['lang'] ) {
507 $dropdown_args['lang'] = $this->pref_lang;
508 }
509
510 return $dropdown_args;
511 }
512 }
513