PluginProbe
Polylang / 3.7
Polylang v3.7
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-classic-editor.php

admin-classic-editor.php in Polylang 3.7, at admin/admin-classic-editor.php

374 lines 11.0 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 defined( 'ABSPATH' ) || exit;
7
8 /**
9 * Manages filters and actions related to the classic editor
10 *
11 * @since 2.4
12 */
13 class PLL_Admin_Classic_Editor {
14 /**
15 * @var PLL_Model
16 */
17 public $model;
18
19 /**
20 * @var PLL_Admin_Links
21 */
22 public $links;
23
24 /**
25 * Current language (used to filter the content).
26 *
27 * @var PLL_Language|null
28 */
29 public $curlang;
30
31 /**
32 * Preferred language to assign to new contents.
33 *
34 * @var PLL_Language|null
35 */
36 public $pref_lang;
37
38 /**
39 * Constructor: setups filters and actions.
40 *
41 * @since 2.4
42 *
43 * @param object $polylang The Polylang object.
44 */
45 public function __construct( &$polylang ) {
46 $this->model = &$polylang->model;
47 $this->links = &$polylang->links;
48 $this->curlang = &$polylang->curlang;
49 $this->pref_lang = &$polylang->pref_lang;
50
51 // Adds the Languages box in the 'Edit Post' and 'Edit Page' panels
52 add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
53
54 // Ajax response for changing the language in the post metabox
55 add_action( 'wp_ajax_post_lang_choice', array( $this, 'post_lang_choice' ) );
56 add_action( 'wp_ajax_pll_posts_not_translated', array( $this, 'ajax_posts_not_translated' ) );
57
58 // Filters the pages by language in the parent dropdown list in the page attributes metabox
59 add_filter( 'page_attributes_dropdown_pages_args', array( $this, 'page_attributes_dropdown_pages_args' ), 10, 2 );
60
61 // Notice
62 add_action( 'edit_form_top', array( $this, 'edit_form_top' ) );
63 }
64
65 /**
66 * Adds the Language box in the 'Edit Post' and 'Edit Page' panels ( as well as in custom post types panels )
67 *
68 * @since 0.1
69 *
70 * @param string $post_type Current post type
71 * @return void
72 */
73 public function add_meta_boxes( $post_type ) {
74 if ( $this->model->is_translated_post_type( $post_type ) ) {
75 add_meta_box(
76 'ml_box',
77 __( 'Languages', 'polylang' ),
78 array( $this, 'post_language' ),
79 $post_type,
80 'side',
81 'high',
82 array(
83 '__back_compat_meta_box' => pll_use_block_editor_plugin(),
84 )
85 );
86 }
87 }
88
89 /**
90 * Displays the Languages metabox in the 'Edit Post' and 'Edit Page' panels
91 *
92 * @since 0.1
93 *
94 * @return void
95 */
96 public function post_language() {
97 global $post_ID;
98 $post_type = get_post_type( $post_ID );
99
100 // phpcs:ignore WordPress.Security.NonceVerification, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
101 $from_post_id = isset( $_GET['from_post'] ) ? (int) $_GET['from_post'] : 0;
102
103 $lang = ( $lg = $this->model->post->get_language( $post_ID ) ) ? $lg :
104 ( isset( $_GET['new_lang'] ) ? $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification
105 $this->pref_lang );
106
107 $dropdown = new PLL_Walker_Dropdown();
108
109 $id = ( 'attachment' === $post_type ) ? sprintf( 'attachments[%d][language]', (int) $post_ID ) : 'post_lang_choice';
110
111 $dropdown_html = $dropdown->walk(
112 $this->model->get_languages_list(),
113 -1,
114 array(
115 'name' => $id,
116 'class' => 'post_lang_choice tags-input',
117 'selected' => $lang ? $lang->slug : '',
118 'flag' => true,
119 )
120 );
121
122 wp_nonce_field( 'pll_language', '_pll_nonce' );
123
124 // NOTE: the class "tags-input" allows to include the field in the autosave $_POST ( see autosave.js )
125 printf(
126 '<p><strong>%1$s</strong></p>
127 <label class="screen-reader-text" for="%2$s">%1$s</label>
128 <div id="select-%3$s-language">%4$s</div>',
129 esc_html__( 'Language', 'polylang' ),
130 esc_attr( $id ),
131 ( 'attachment' === $post_type ? 'media' : 'post' ),
132 $dropdown_html // phpcs:ignore WordPress.Security.EscapeOutput
133 );
134
135 /**
136 * Fires before displaying the list of translations in the Languages metabox for posts
137 *
138 * @since 1.8
139 */
140 do_action( 'pll_before_post_translations', $post_type );
141
142 echo '<div id="post-translations" class="translations">';
143 if ( $lang ) {
144 if ( 'attachment' === $post_type ) {
145 include __DIR__ . '/view-translations-media.php';
146 } else {
147 include __DIR__ . '/view-translations-post.php';
148 }
149 }
150 echo '</div>' . "\n";
151 }
152
153 /**
154 * Ajax response for changing the language in the post metabox
155 *
156 * @since 0.2
157 *
158 * @return void
159 */
160 public function post_lang_choice() {
161 check_ajax_referer( 'pll_language', '_pll_nonce' );
162
163 if ( ! isset( $_POST['post_id'], $_POST['lang'], $_POST['post_type'] ) ) {
164 wp_die( 'The request is missing the parameter "post_type", "lang" and/or "post_id".' );
165 }
166
167 global $post_ID; // Obliged to use the global variable for wp_popular_terms_checklist
168 $post_ID = (int) $_POST['post_id'];
169 $lang_slug = sanitize_key( $_POST['lang'] );
170 $lang = $this->model->get_language( $lang_slug );
171 $post_type = sanitize_key( $_POST['post_type'] );
172
173 if ( empty( $lang ) ) {
174 wp_die( esc_html( "{$lang_slug} is not a valid language code." ) );
175 }
176
177 $post_type_object = get_post_type_object( $post_type );
178
179 if ( empty( $post_type_object ) ) {
180 wp_die( esc_html( "{$post_type} is not a valid post type." ) );
181 }
182
183 if ( ! current_user_can( $post_type_object->cap->edit_post, $post_ID ) ) {
184 wp_die( 'You are not allowed to edit this post.' );
185 }
186
187 $this->model->post->set_language( $post_ID, $lang );
188
189 ob_start();
190 if ( 'attachment' === $post_type ) {
191 include __DIR__ . '/view-translations-media.php';
192 } else {
193 include __DIR__ . '/view-translations-post.php';
194 }
195 $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
196 ob_end_clean();
197
198 // Categories
199 if ( isset( $_POST['taxonomies'] ) ) { // Not set for pages
200 $supplemental = array();
201
202 foreach ( array_map( 'sanitize_key', $_POST['taxonomies'] ) as $taxname ) {
203 $taxonomy = get_taxonomy( $taxname );
204
205 if ( ! empty( $taxonomy ) ) {
206 ob_start();
207 $popular_ids = wp_popular_terms_checklist( $taxonomy->name );
208 $supplemental['populars'] = ob_get_contents();
209 ob_end_clean();
210
211 ob_start();
212 // Use $post_ID to remember checked terms in case we come back to the original language
213 wp_terms_checklist( $post_ID, array( 'taxonomy' => $taxonomy->name, 'popular_cats' => $popular_ids ) );
214 $supplemental['all'] = ob_get_contents();
215 ob_end_clean();
216
217 $supplemental['dropdown'] = wp_dropdown_categories(
218 array(
219 'taxonomy' => $taxonomy->name,
220 'hide_empty' => 0,
221 'name' => 'new' . $taxonomy->name . '_parent',
222 'orderby' => 'name',
223 'hierarchical' => 1,
224 'show_option_none' => '&mdash; ' . $taxonomy->labels->parent_item . ' &mdash;',
225 'echo' => 0,
226 )
227 );
228
229 $x->Add( array( 'what' => 'taxonomy', 'data' => $taxonomy->name, 'supplemental' => $supplemental ) );
230 }
231 }
232 }
233
234 // Parent dropdown list ( only for hierarchical post types )
235 if ( in_array( $post_type, get_post_types( array( 'hierarchical' => true ) ) ) ) {
236 $post = get_post( $post_ID );
237
238 if ( ! empty( $post ) ) {
239 // Args and filter from 'page_attributes_meta_box' in wp-admin/includes/meta-boxes.php of WP 4.2.1
240 $dropdown_args = array(
241 'post_type' => $post->post_type,
242 'exclude_tree' => $post->ID,
243 'selected' => $post->post_parent,
244 'name' => 'parent_id',
245 'show_option_none' => __( '(no parent)', 'polylang' ),
246 'sort_column' => 'menu_order, post_title',
247 'echo' => 0,
248 );
249
250 /** This filter is documented in wp-admin/includes/meta-boxes.php */
251 $dropdown_args = (array) apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); // Since WP 3.3.
252 $dropdown_args['echo'] = 0; // Make sure to not print it.
253
254 /** @var string $data */
255 $data = wp_dropdown_pages( $dropdown_args ); // phpcs:ignore WordPress.Security.EscapeOutput
256
257 $x->Add( array( 'what' => 'pages', 'data' => $data ) );
258 }
259 }
260
261 // Flag
262 $x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
263
264 // Sample permalink
265 $x->Add( array( 'what' => 'permalink', 'data' => get_sample_permalink_html( $post_ID ) ) );
266
267 $x->send();
268 }
269
270 /**
271 * Ajax response for input in translation autocomplete input box
272 *
273 * @since 1.5
274 *
275 * @return void
276 */
277 public function ajax_posts_not_translated() {
278 check_ajax_referer( 'pll_language', '_pll_nonce' );
279
280 if ( ! isset( $_GET['post_type'], $_GET['post_language'], $_GET['translation_language'], $_GET['term'], $_GET['pll_post_id'] ) ) {
281 wp_die( 0 );
282 }
283
284 $post_type = sanitize_key( $_GET['post_type'] );
285
286 if ( ! post_type_exists( $post_type ) ) {
287 wp_die( 0 );
288 }
289
290 $term = wp_unslash( $_GET['term'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
291
292 $post_language = $this->model->get_language( sanitize_key( $_GET['post_language'] ) );
293 $translation_language = $this->model->get_language( sanitize_key( $_GET['translation_language'] ) );
294
295 $return = array();
296
297 $untranslated_posts = $this->model->post->get_untranslated( $post_type, $post_language, $translation_language, $term );
298
299 // format output
300 foreach ( $untranslated_posts as $post ) {
301 $return[] = array(
302 'id' => $post->ID,
303 'value' => $post->post_title,
304 'link' => $this->links->edit_post_translation_link( $post->ID ),
305 );
306 }
307
308 // Add current translation in list
309 if ( $post_id = $this->model->post->get_translation( (int) $_GET['pll_post_id'], $translation_language ) ) {
310 $post = get_post( $post_id );
311
312 if ( ! empty( $post ) ) {
313 array_unshift(
314 $return,
315 array(
316 'id' => $post_id,
317 'value' => $post->post_title,
318 'link' => $this->links->edit_post_translation_link( $post_id ),
319 )
320 );
321 }
322 }
323
324 wp_die( wp_json_encode( $return ) );
325 }
326
327 /**
328 * Filters the pages by language in the parent dropdown list in the page attributes metabox.
329 *
330 * @since 0.6
331 *
332 * @param array $dropdown_args Arguments passed to wp_dropdown_pages().
333 * @param WP_Post $post The page being edited.
334 * @return array Modified arguments.
335 */
336 public function page_attributes_dropdown_pages_args( $dropdown_args, $post ) {
337 $language = isset( $_POST['lang'] ) ? $this->model->get_language( sanitize_key( $_POST['lang'] ) ) : $this->model->post->get_language( $post->ID ); // phpcs:ignore WordPress.Security.NonceVerification
338
339 if ( empty( $language ) ) {
340 $language = $this->pref_lang;
341 }
342
343 if ( ! empty( $language ) ) {
344 $dropdown_args['lang'] = $language->slug;
345 }
346
347 return $dropdown_args;
348 }
349
350 /**
351 * Displays a notice if the user has not sufficient rights to overwrite synchronized taxonomies and metas.
352 *
353 * @since 2.6
354 *
355 * @param WP_Post $post the post currently being edited.
356 * @return void
357 */
358 public function edit_form_top( $post ) {
359 if ( ! $this->model->post->current_user_can_synchronize( $post->ID ) ) {
360 ?>
361 <div class="pll-notice notice notice-warning">
362 <p>
363 <?php
364 esc_html_e( 'Some taxonomies or metadata may be synchronized with existing translations that you are not allowed to modify.', 'polylang' );
365 echo ' ';
366 esc_html_e( 'If you attempt to modify them anyway, your changes will not be saved.', 'polylang' );
367 ?>
368 </p>
369 </div>
370 <?php
371 }
372 }
373 }
374