PluginProbe
Polylang / 2.6.2
Polylang v2.6.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-columns.php

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

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