PluginProbe
Polylang / 2.9.1
Polylang v2.9.1
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-columns.php

admin-filters-columns.php in Polylang 2.9.1, at admin/admin-filters-columns.php

412 lines 14.7 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 /**
7 * Adds the language column in posts and terms list tables
8 * Manages quick edit and bulk edit as well
9 *
10 * @since 1.2
11 */
12 class PLL_Admin_Filters_Columns {
13 public $links, $model, $filter_lang;
14
15 /**
16 * Constructor: setups filters and actions
17 *
18 * @since 1.2
19 *
20 * @param object $polylang
21 */
22 public function __construct( &$polylang ) {
23 $this->links = &$polylang->links;
24 $this->model = &$polylang->model;
25 $this->filter_lang = &$polylang->filter_lang;
26
27 // Hide the column of the filtered language.
28 add_filter( 'hidden_columns', array( $this, 'hidden_columns' ) ); // Since WP 4.4.
29
30 // Add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels.
31 foreach ( $this->model->get_translated_post_types() as $type ) {
32 // Use the latest filter late as some plugins purely overwrite what's done by others :(
33 // Specific case for media.
34 add_filter( 'manage_' . ( 'attachment' == $type ? 'upload' : 'edit-' . $type ) . '_columns', array( $this, 'add_post_column' ), 100 );
35 add_action( 'manage_' . ( 'attachment' == $type ? 'media' : $type . '_posts' ) . '_custom_column', array( $this, 'post_column' ), 10, 2 );
36 }
37
38 // Quick edit and bulk edit.
39 add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ) );
40 add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ) );
41
42 // Adds the language column in the 'Categories' and 'Post Tags' tables.
43 foreach ( $this->model->get_translated_taxonomies() as $tax ) {
44 add_filter( 'manage_edit-' . $tax . '_columns', array( $this, 'add_term_column' ) );
45 add_filter( 'manage_' . $tax . '_custom_column', array( $this, 'term_column' ), 10, 3 );
46 }
47
48 // Ajax responses to update list table rows.
49 add_action( 'wp_ajax_pll_update_post_rows', array( $this, 'ajax_update_post_rows' ) );
50 add_action( 'wp_ajax_pll_update_term_rows', array( $this, 'ajax_update_term_rows' ) );
51 }
52
53 /**
54 * Adds languages and translations columns in posts, pages, media, categories and tags tables
55 *
56 * @since 0.8.2
57 *
58 * @param array $columns List of table columns
59 * @param string $before The column before which we want to add our languages
60 * @return array modified list of columns
61 */
62 protected function add_column( $columns, $before ) {
63 if ( $n = array_search( $before, array_keys( $columns ) ) ) {
64 $end = array_slice( $columns, $n );
65 $columns = array_slice( $columns, 0, $n );
66 }
67
68 foreach ( $this->model->get_languages_list() as $language ) {
69 $columns[ 'language_' . $language->slug ] = $this->get_flag_html( $language ) . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>';
70 }
71
72 return isset( $end ) ? array_merge( $columns, $end ) : $columns;
73 }
74
75 /**
76 * Returns the first language column in the posts, pages and media library tables
77 *
78 * @since 0.9
79 *
80 * @return string first language column name
81 */
82 protected function get_first_language_column() {
83 $columns = array();
84
85 foreach ( $this->model->get_languages_list() as $language ) {
86 $columns[] = 'language_' . $language->slug;
87 }
88
89 return empty( $columns ) ? '' : reset( $columns );
90 }
91
92 /**
93 * Hide the column for the filtered language
94 *
95 * @since 2.7
96 *
97 * @param array $hidden Array of hidden columns
98 * @return array
99 */
100 public function hidden_columns( $hidden ) {
101 if ( ! empty( $this->filter_lang ) ) {
102 $hidden[] = 'language_' . $this->filter_lang->slug;
103 }
104 return $hidden;
105 }
106
107 /**
108 * Adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables
109 *
110 * @since 0.1
111 *
112 * @param array $columns list of posts table columns
113 * @return array modified list of columns
114 */
115 public function add_post_column( $columns ) {
116 return $this->add_column( $columns, 'comments' );
117 }
118
119 /**
120 * Fills the language and translations columns in the posts, pages and media library tables
121 * take care that when doing ajax inline edit, the post may not be updated in database yet
122 *
123 * @since 0.1
124 *
125 * @param string $column Column name
126 * @param int $post_id
127 */
128 public function post_column( $column, $post_id ) {
129 $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
130 $lang = $inline ? $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) : $this->model->post->get_language( $post_id ); // phpcs:ignore WordPress.Security.NonceVerification
131
132 if ( false === strpos( $column, 'language_' ) || ! $lang ) {
133 return;
134 }
135
136 $language = $this->model->get_language( substr( $column, 9 ) );
137
138 // Hidden field containing the post language for quick edit
139 if ( $column == $this->get_first_language_column() ) {
140 printf( '<div class="hidden" id="lang_%d">%s</div>', intval( $post_id ), esc_html( $lang->slug ) );
141 }
142
143 // Link to edit post ( or a translation )
144 if ( $id = $this->model->post->get( $post_id, $language ) ) {
145 // get_edit_post_link returns nothing if the user cannot edit the post
146 // Thanks to Solinx. See http://wordpress.org/support/topic/feature-request-incl-code-check-for-capabilities-in-admin-screens
147 if ( $link = get_edit_post_link( $id ) ) {
148 $flag = '';
149 if ( $id === $post_id ) {
150 $flag = $this->get_flag_html( $language );
151 $class = 'pll_column_flag';
152 /* translators: accessibility text, %s is a native language name */
153 $s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
154 } else {
155 $class = esc_attr( 'pll_icon_edit translation_' . $id );
156 /* translators: accessibility text, %s is a native language name */
157 $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
158 }
159 printf(
160 '<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span>%5$s</a>',
161 esc_attr( $class ),
162 esc_attr( get_post( $id )->post_title ),
163 esc_url( $link ),
164 esc_html( $s ),
165 $flag // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
166 );
167 } elseif ( $id === $post_id ) {
168 printf(
169 '<span class="pll_column_flag" style=""><span class="screen-reader-text">%1$s</span>%2$s</span>',
170 /* translators: accessibility text, %s is a native language name */
171 esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) ),
172 $this->get_flag_html( $language ) // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
173 );
174 }
175 }
176 // Link to add a new translation
177 else {
178 echo $this->links->new_post_translation_link( $post_id, $language ); // phpcs:ignore WordPress.Security.EscapeOutput
179 }
180 }
181
182 /**
183 * Quick edit & bulk edit
184 *
185 * @since 0.9
186 *
187 * @param string $column column name
188 * @return string unmodified $column
189 */
190 public function quick_edit_custom_box( $column ) {
191 if ( $column == $this->get_first_language_column() ) {
192
193 $elements = $this->model->get_languages_list();
194 if ( current_filter() == 'bulk_edit_custom_box' ) {
195 array_unshift( $elements, (object) array( 'slug' => -1, 'name' => __( '&mdash; No Change &mdash;', 'polylang' ) ) );
196 }
197
198 $dropdown = new PLL_Walker_Dropdown();
199 // The hidden field 'old_lang' allows to pass the old language to ajax request
200 printf(
201 '<fieldset class="inline-edit-col-left">
202 <div class="inline-edit-col">
203 <label class="alignleft">
204 <span class="title">%s</span>
205 %s
206 </label>
207 </div>
208 </fieldset>',
209 esc_html__( 'Language', 'polylang' ),
210 $dropdown->walk( $elements, -1, array( 'name' => 'inline_lang_choice', 'id' => '' ) ) // phpcs:ignore WordPress.Security.EscapeOutput
211 );
212 }
213 return $column;
214 }
215
216 /**
217 * Adds the language column ( before the posts column ) in the 'Categories' or 'Post Tags' table
218 *
219 * @since 0.1
220 *
221 * @param array $columns list of terms table columns
222 * @return array modified list of columns
223 */
224 public function add_term_column( $columns ) {
225 return $this->add_column( $columns, 'posts' );
226 }
227
228 /**
229 * Fills the language column in the 'Categories' or 'Post Tags' table
230 *
231 * @since 0.1
232 *
233 * @param string $out
234 * @param string $column Column name
235 * @param int $term_id
236 */
237 public function term_column( $out, $column, $term_id ) {
238 $inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save-tax' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
239 if ( false === strpos( $column, 'language_' ) || ! ( $lang = $inline ? $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) : $this->model->term->get_language( $term_id ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
240 return $out;
241 }
242
243 if ( isset( $_REQUEST['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
244 $post_type = sanitize_key( $_REQUEST['post_type'] ); // phpcs:ignore WordPress.Security.NonceVerification
245 }
246
247 if ( isset( $GLOBALS['post_type'] ) ) {
248 $post_type = $GLOBALS['post_type'];
249 }
250
251 if ( isset( $_REQUEST['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
252 $taxonomy = sanitize_key( $_REQUEST['taxonomy'] ); // phpcs:ignore WordPress.Security.NonceVerification
253 }
254
255 if ( isset( $GLOBALS['taxonomy'] ) ) {
256 $taxonomy = $GLOBALS['taxonomy'];
257 }
258
259 if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
260 return $out;
261 }
262
263 $term_id = (int) $term_id;
264 $language = $this->model->get_language( substr( $column, 9 ) );
265
266 if ( $column == $this->get_first_language_column() ) {
267 $out = sprintf( '<div class="hidden" id="lang_%d">%s</div>', intval( $term_id ), esc_html( $lang->slug ) );
268
269 // Identify the default categories to disable the language dropdown in js
270 if ( in_array( get_option( 'default_category' ), $this->model->term->get_translations( $term_id ) ) ) {
271 $out .= sprintf( '<div class="hidden" id="default_cat_%1$d">%1$d</div>', intval( $term_id ) );
272 }
273 }
274
275 // Link to edit term ( or a translation )
276 if ( ( $id = $this->model->term->get( $term_id, $language ) ) && $term = get_term( $id, $taxonomy ) ) {
277 if ( $link = get_edit_term_link( $id, $taxonomy, $post_type ) ) {
278 $flag = '';
279 if ( $id === $term_id ) {
280 $flag = $this->get_flag_html( $language );
281 $class = 'pll_column_flag';
282 /* translators: accessibility text, %s is a native language name */
283 $s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
284 } else {
285 $class = esc_attr( 'pll_icon_edit translation_' . $id );
286 /* translators: accessibility text, %s is a native language name */
287 $s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
288 }
289 $out .= sprintf(
290 '<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span>%5$s</a>',
291 $class,
292 esc_attr( $term->name ),
293 esc_url( $link ),
294 esc_html( $s ),
295 $flag // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
296 );
297 } elseif ( $id === $term_id ) {
298 $out .= sprintf(
299 '<span class="pll_column_flag"><span class="screen-reader-text">%1$s</span>%2$s</span>',
300 /* translators: accessibility text, %s is a native language name */
301 esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) ),
302 $this->get_flag_html( $language ) // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
303 );
304 }
305 }
306
307 // Link to add a new translation
308 else {
309 $out .= $this->links->new_term_translation_link( $term_id, $taxonomy, $post_type, $language );
310 }
311
312 return $out;
313 }
314
315 /**
316 * Update rows of translated posts when the language is modified in quick edit
317 *
318 * @since 1.7
319 */
320 public function ajax_update_post_rows() {
321 check_ajax_referer( 'inlineeditnonce', '_pll_nonce' );
322
323 if ( ! isset( $_POST['post_type'], $_POST['post_id'], $_POST['screen'] ) ) {
324 wp_die( 0 );
325 }
326
327 $post_type = sanitize_key( $_POST['post_type'] );
328
329 if ( ! post_type_exists( $post_type ) || ! $this->model->is_translated_post_type( $post_type ) ) {
330 wp_die( 0 );
331 }
332
333 global $wp_list_table;
334 $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
335
336 $x = new WP_Ajax_Response();
337
338 // Collect old translations
339 $translations = empty( $_POST['translations'] ) ? array() : explode( ',', $_POST['translations'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
340 $translations = array_map( 'intval', $translations );
341
342 $translations = array_merge( $translations, array( (int) $_POST['post_id'] ) ); // Add current post
343
344 foreach ( $translations as $post_id ) {
345 $level = is_post_type_hierarchical( $post_type ) ? count( get_ancestors( $post_id, $post_type ) ) : 0;
346 if ( $post = get_post( $post_id ) ) {
347 ob_start();
348 $wp_list_table->single_row( $post, $level );
349 $data = ob_get_clean();
350 $x->add( array( 'what' => 'row', 'data' => $data, 'supplemental' => array( 'post_id' => $post_id ) ) );
351 }
352 }
353
354 $x->send();
355 }
356
357 /**
358 * Update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit
359 *
360 * @since 1.7
361 */
362 public function ajax_update_term_rows() {
363 check_ajax_referer( 'pll_language', '_pll_nonce' );
364
365 if ( ! isset( $_POST['taxonomy'], $_POST['term_id'], $_POST['screen'] ) ) {
366 wp_die( 0 );
367 }
368
369 $taxonomy = sanitize_key( $_POST['taxonomy'] );
370
371 if ( ! taxonomy_exists( $taxonomy ) || ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
372 wp_die( 0 );
373 }
374
375 global $wp_list_table;
376 $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
377
378 $x = new WP_Ajax_Response();
379
380 // Collect old translations
381 $translations = empty( $_POST['translations'] ) ? array() : explode( ',', $_POST['translations'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
382 $translations = array_map( 'intval', $translations );
383
384 $translations = array_merge( $translations, $this->model->term->get_translations( (int) $_POST['term_id'] ) ); // Add current translations
385 $translations = array_unique( $translations ); // Remove duplicates
386
387 foreach ( $translations as $term_id ) {
388 $level = is_taxonomy_hierarchical( $taxonomy ) ? count( get_ancestors( $term_id, $taxonomy ) ) : 0;
389 if ( $tag = get_term( $term_id, $taxonomy ) ) {
390 ob_start();
391 $wp_list_table->single_row( $tag, $level );
392 $data = ob_get_clean();
393 $x->add( array( 'what' => 'row', 'data' => $data, 'supplemental' => array( 'term_id' => $term_id ) ) );
394 }
395 }
396
397 $x->send();
398 }
399
400 /**
401 * Returns the language flag or teh language slug if there is no flag.
402 *
403 * @since 2.8
404 *
405 * @param object $language PLL_Language object.
406 * @return string
407 */
408 protected function get_flag_html( $language ) {
409 return $language->flag ? $language->flag : sprintf( '<abbr>%s</abbr>', esc_html( $language->slug ) );
410 }
411 }
412