PluginProbe
Polylang / 3.8
Polylang v3.8
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 3.8, at src/admin/admin-classic-editor.php

386 lines 11.1 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 $post_type_object = get_post_type_object( $post->post_type );
194
195 if ( empty( $post_type_object ) ) {
196 wp_die( esc_html( "{$post->post_type} is not a valid post type." ) );
197 }
198
199 if ( ! current_user_can( $post_type_object->cap->edit_post, $post->ID ) ) {
200 wp_die( 'You are not allowed to edit this post.' );
201 }
202
203 $this->model->post->set_language( $post->ID, $lang );
204
205 ob_start();
206 if ( 'attachment' === $post->post_type ) {
207 include __DIR__ . '/view-translations-media.php';
208 } else {
209 include __DIR__ . '/view-translations-post.php';
210 }
211 $x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
212 ob_end_clean();
213
214 // Categories
215 if ( isset( $_POST['taxonomies'] ) ) { // Not set for pages
216 $supplemental = array();
217
218 foreach ( array_map( 'sanitize_key', $_POST['taxonomies'] ) as $taxname ) {
219 $taxonomy = get_taxonomy( $taxname );
220
221 if ( ! empty( $taxonomy ) ) {
222 ob_start();
223 $popular_ids = wp_popular_terms_checklist( $taxonomy->name );
224 $supplemental['populars'] = ob_get_contents();
225 ob_end_clean();
226
227 ob_start();
228 // Use $post->ID to remember checked terms in case we come back to the original language
229 wp_terms_checklist( $post->ID, array( 'taxonomy' => $taxonomy->name, 'popular_cats' => $popular_ids ) );
230 $supplemental['all'] = ob_get_contents();
231 ob_end_clean();
232
233 $supplemental['dropdown'] = wp_dropdown_categories(
234 array(
235 'taxonomy' => $taxonomy->name,
236 'hide_empty' => 0,
237 'name' => 'new' . $taxonomy->name . '_parent',
238 'orderby' => 'name',
239 'hierarchical' => 1,
240 'show_option_none' => '&mdash; ' . $taxonomy->labels->parent_item . ' &mdash;',
241 'echo' => 0,
242 )
243 );
244
245 $x->Add( array( 'what' => 'taxonomy', 'data' => $taxonomy->name, 'supplemental' => $supplemental ) );
246 }
247 }
248 }
249
250 // Parent dropdown list ( only for hierarchical post types )
251 if ( in_array( $post->post_type, get_post_types( array( 'hierarchical' => true ) ) ) ) {
252 // Args and filter from 'page_attributes_meta_box' in wp-admin/includes/meta-boxes.php of WP 4.2.1
253 $dropdown_args = array(
254 'post_type' => $post->post_type,
255 'exclude_tree' => $post->ID,
256 'selected' => $post->post_parent,
257 'name' => 'parent_id',
258 'show_option_none' => __( '(no parent)', 'polylang' ),
259 'sort_column' => 'menu_order, post_title',
260 'echo' => 0,
261 );
262
263 /** This filter is documented in wp-admin/includes/meta-boxes.php */
264 $dropdown_args = (array) apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); // Since WP 3.3.
265 $dropdown_args['echo'] = 0; // Make sure to not print it.
266
267 /** @var string $data */
268 $data = wp_dropdown_pages( $dropdown_args ); // phpcs:ignore WordPress.Security.EscapeOutput
269
270 $x->Add( array( 'what' => 'pages', 'data' => $data ) );
271 }
272
273 // Flag
274 $x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
275
276 // Sample permalink
277 $x->Add( array( 'what' => 'permalink', 'data' => get_sample_permalink_html( $post->ID ) ) );
278
279 $x->send();
280 }
281
282 /**
283 * Ajax response for input in translation autocomplete input box
284 *
285 * @since 1.5
286 *
287 * @return void
288 */
289 public function ajax_posts_not_translated() {
290 check_ajax_referer( 'pll_language', '_pll_nonce' );
291
292 if ( ! isset( $_GET['post_type'], $_GET['post_language'], $_GET['translation_language'], $_GET['term'], $_GET['pll_post_id'] ) ) {
293 wp_die( 0 );
294 }
295
296 $post_type = sanitize_key( $_GET['post_type'] );
297
298 if ( ! post_type_exists( $post_type ) ) {
299 wp_die( 0 );
300 }
301
302 $term = wp_unslash( $_GET['term'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
303
304 $post_language = $this->model->get_language( sanitize_key( $_GET['post_language'] ) );
305 $translation_language = $this->model->get_language( sanitize_key( $_GET['translation_language'] ) );
306
307 $return = array();
308
309 $untranslated_posts = $this->model->post->get_untranslated( $post_type, $post_language, $translation_language, $term );
310
311 // format output
312 foreach ( $untranslated_posts as $post ) {
313 $return[] = array(
314 'id' => $post->ID,
315 'value' => $post->post_title,
316 'link' => $this->links->get_edit_post_link_html( $post ),
317 );
318 }
319
320 // Add current translation in list
321 if ( $post_id = $this->model->post->get_translation( (int) $_GET['pll_post_id'], $translation_language ) ) {
322 $post = get_post( $post_id );
323
324 if ( ! empty( $post ) ) {
325 array_unshift(
326 $return,
327 array(
328 'id' => $post_id,
329 'value' => $post->post_title,
330 'link' => $this->links->get_edit_post_link_html( $post ),
331 )
332 );
333 }
334 }
335
336 wp_die( wp_json_encode( $return ) );
337 }
338
339 /**
340 * Filters the pages by language in the parent dropdown list in the page attributes metabox.
341 *
342 * @since 0.6
343 *
344 * @param array $dropdown_args Arguments passed to wp_dropdown_pages().
345 * @param WP_Post $post The page being edited.
346 * @return array Modified arguments.
347 */
348 public function page_attributes_dropdown_pages_args( $dropdown_args, $post ) {
349 $language = isset( $_POST['lang'] ) ? $this->model->get_language( sanitize_key( $_POST['lang'] ) ) : $this->model->post->get_language( $post->ID ); // phpcs:ignore WordPress.Security.NonceVerification
350
351 if ( empty( $language ) ) {
352 $language = $this->pref_lang;
353 }
354
355 if ( ! empty( $language ) ) {
356 $dropdown_args['lang'] = $language->slug;
357 }
358
359 return $dropdown_args;
360 }
361
362 /**
363 * Displays a notice if the user has not sufficient rights to overwrite synchronized taxonomies and metas.
364 *
365 * @since 2.6
366 *
367 * @param WP_Post $post the post currently being edited.
368 * @return void
369 */
370 public function edit_form_top( $post ) {
371 if ( ! $this->model->post->current_user_can_synchronize( $post->ID ) ) {
372 ?>
373 <div class="pll-notice notice notice-warning">
374 <p>
375 <?php
376 esc_html_e( 'Some taxonomies or metadata may be synchronized with existing translations that you are not allowed to modify.', 'polylang' );
377 echo ' ';
378 esc_html_e( 'If you attempt to modify them anyway, your changes will not be saved.', 'polylang' );
379 ?>
380 </p>
381 </div>
382 <?php
383 }
384 }
385 }
386