PluginProbe
Polylang / trunk
Polylang vtrunk
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 / src / admin / admin-classic-editor.php

admin-classic-editor.php in Polylang trunk, at src/admin/admin-classic-editor.php

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