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-links.php

admin-links.php in Polylang 3.8, at src/admin/admin-links.php

439 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 use WP_Syntex\Polylang\Capabilities\Capabilities;
7
8 /**
9 * Manages links related functions.
10 *
11 * @since 1.8
12 */
13 class PLL_Admin_Links extends PLL_Links {
14 /**
15 * Current user.
16 *
17 * @var \WP_Syntex\Polylang\Capabilities\User\User_Interface
18 */
19 protected $user;
20
21 /**
22 * Constructor.
23 *
24 * @since 3.8
25 *
26 * @param PLL_Base $polylang The Polylang object.
27 */
28 public function __construct( PLL_Base &$polylang ) {
29 parent::__construct( $polylang );
30 $this->user = Capabilities::get_user();
31 }
32
33 /**
34 * Returns the html markup for a new post translation link.
35 *
36 * @since 3.8
37 *
38 * @param WP_Post $post The source post.
39 * @param PLL_Language $language The language of the new translation.
40 * @return string
41 */
42 public function get_new_post_link_html( WP_Post $post, PLL_Language $language ): string {
43 $link = $this->get_new_post_translation_link( $post, $language );
44 return $this->new_translation_link( $link, $language );
45 }
46
47 /**
48 * Returns the URL to create a new post translation.
49 * Returns an empty string if the current user is not allowed to create posts in the given language.
50 *
51 * @since 1.5
52 * @since 3.8 Changed first parameter type from `int` to `WP_Post`.
53 *
54 * @param WP_Post $post The source post.
55 * @param PLL_Language $language The language of the new translation.
56 * @param string $context Optional. Defaults to 'display' which encodes '&' to '&amp;'.
57 * Otherwise, preserves '&'.
58 * @return string
59 */
60 public function get_new_post_translation_link( WP_Post $post, PLL_Language $language, string $context = 'display' ): string {
61 if ( ! $this->user->can_translate( $language ) ) {
62 return '';
63 }
64
65 $post_type_object = get_post_type_object( $post->post_type );
66
67 if ( empty( $post_type_object ) || ! $this->user->has_cap( $post_type_object->cap->create_posts ) ) {
68 return '';
69 }
70
71 // Special case for the privacy policy page which is associated to a specific capability
72 if ( 'page' === $post_type_object->name && ! $this->user->has_cap( 'manage_privacy_options' ) ) {
73 $privacy_page = get_option( 'wp_page_for_privacy_policy' );
74 $privacy_page = is_numeric( $privacy_page ) ? (int) $privacy_page : 0;
75
76 if ( $privacy_page && in_array( $post->ID, $this->model->post->get_translations( $privacy_page ) ) ) {
77 return '';
78 }
79 }
80
81 if ( 'attachment' === $post->post_type ) {
82 $args = array(
83 'action' => 'translate_media',
84 'from_media' => $post->ID,
85 'new_lang' => $language->slug,
86 );
87
88 $link = add_query_arg( $args, admin_url( 'admin.php' ) );
89
90 // Add nonce for media as we will directly publish a new attachment from a click on this link
91 if ( 'display' === $context ) {
92 $link = wp_nonce_url( $link, 'translate_media' );
93 } else {
94 $link = add_query_arg( '_wpnonce', wp_create_nonce( 'translate_media' ), $link );
95 }
96 } else {
97 $args = array(
98 'post_type' => $post->post_type,
99 'from_post' => $post->ID,
100 'new_lang' => $language->slug,
101 );
102
103 $link = add_query_arg( $args, admin_url( 'post-new.php' ) );
104
105 if ( 'display' === $context ) {
106 $link = wp_nonce_url( $link, 'new-post-translation' );
107 } else {
108 $link = add_query_arg( '_wpnonce', wp_create_nonce( 'new-post-translation' ), $link );
109 }
110 }
111
112 /**
113 * Filters the new post translation link.
114 *
115 * @since 1.8
116 *
117 * @param string $link The new post translation link.
118 * @param PLL_Language $language The language of the new translation.
119 * @param int $post_id The source post id.
120 */
121 return apply_filters( 'pll_get_new_post_translation_link', $link, $language, $post->ID );
122 }
123
124 /**
125 * Returns the html markup for a new translation link.
126 *
127 * @since 2.6
128 *
129 * @param string $link The new translation link.
130 * @param PLL_Language $language The language of the new translation.
131 * @return string
132 */
133 protected function new_translation_link( string $link, PLL_Language $language ): string {
134 if ( empty( $link ) ) {
135 return sprintf(
136 '<span title="%s" class="pll_icon_add wp-ui-text-icon"></span>',
137 /* translators: accessibility text, %s is a native language name */
138 esc_attr( sprintf( __( 'You are not allowed to add a translation in %s', 'polylang' ), $language->name ) )
139 );
140 }
141
142 /* translators: accessibility text, %s is a native language name */
143 $hint = sprintf( __( 'Add a translation in %s', 'polylang' ), $language->name );
144 return sprintf(
145 '<a href="%1$s" title="%2$s" class="pll_icon_add"><span class="screen-reader-text">%3$s</span></a>',
146 esc_url( $link ),
147 esc_attr( $hint ),
148 esc_html( $hint )
149 );
150 }
151
152 /**
153 * Returns the html markup for a post translation link.
154 *
155 * @since 3.8
156 *
157 * @param WP_Post $post The post.
158 * @param string $mode Optional. How the link should be displayed: with a pen icon or a language's flag.
159 * Possible values are:
160 * - `metabox_translation` (pen icon in metabox),
161 * - `list_translation` (pen icon in items list),
162 * - `list_current` (flag in items list).
163 * Default is `metabox_translation`.
164 * @return string
165 *
166 * @phpstan-param 'metabox_translation'|'list_translation'|'list_current' $mode
167 */
168 public function get_edit_post_link_html( WP_Post $post, string $mode = 'metabox_translation' ): string {
169 $language = $this->model->post->get_language( $post->ID );
170
171 if ( empty( $language ) ) {
172 // Should not happen.
173 return '';
174 }
175
176 $url = (string) get_edit_post_link( $post->ID );
177 return $this->get_edit_item_link_html( $url, $language, $post->ID, $post->post_title, $mode );
178 }
179
180 /**
181 * Returns the html markup for a translation link.
182 *
183 * @since 3.8
184 *
185 * @param string $url URL of the edition link.
186 * @param PLL_Language $language Language of the item.
187 * @param int $item_id ID of the item. Used only in `list_translation` mode.
188 * @param string $item_name Name of the item. Not used in `metabox_translation` mode.
189 * @param string $mode How the link should be displayed: with a pen icon or a language's flag.
190 * Possible values are:
191 * - `metabox_translation` (pen icon in metabox),
192 * - `list_translation` (pen icon in items list),
193 * - `list_current` (flag in items list).
194 * Default is `metabox_translation`.
195 * @return string
196 *
197 * @phpstan-param 'metabox_translation'|'list_translation'|'list_current' $mode
198 */
199 private function get_edit_item_link_html( string $url, PLL_Language $language, int $item_id, string $item_name, string $mode ): string {
200 if ( 'list_current' === $mode ) {
201 $flag = $this->get_flag_html( $language );
202 $class = 'pll_column_flag';
203 } else {
204 $flag = '';
205 $class = 'pll_icon_edit';
206 }
207
208 if ( empty( $url ) ) {
209 // The current user is not allowed to edit the item.
210 if ( 'list_current' === $mode ) {
211 /* translators: accessibility text, %s is a native language name */
212 $hint = sprintf( __( 'You are not allowed to edit this item in %s', 'polylang' ), $language->name );
213 } else {
214 /* translators: accessibility text, %s is a native language name */
215 $hint = sprintf( __( 'You are not allowed to edit a translation in %s', 'polylang' ), $language->name );
216 }
217
218 return sprintf(
219 '<span title="%s" class="%s wp-ui-text-icon">%s</span>',
220 esc_attr( $hint ),
221 esc_attr( $class ),
222 $flag // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
223 );
224 }
225
226 // The current user is allowed to edit the item.
227 if ( 'list_current' === $mode ) {
228 /* translators: accessibility text, %s is a native language name */
229 $hint = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
230 } elseif ( 'list_translation' === $mode ) {
231 /* translators: accessibility text, %s is a native language name */
232 $hint = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
233 $class .= " translation_{$item_id}";
234 } else {
235 /* translators: accessibility text, %s is a native language name */
236 $hint = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
237 $item_name = $hint;
238 }
239
240 return sprintf(
241 '<a href="%s" class="%s" title="%s"><span class="screen-reader-text">%s</span>%s</a>',
242 esc_url( $url ),
243 esc_attr( $class ),
244 esc_attr( $item_name ),
245 esc_html( $hint ),
246 $flag // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
247 );
248 }
249
250 /**
251 * Returns the html markup for a new term translation.
252 *
253 * @since 3.8
254 *
255 * @param WP_Term $term The source term.
256 * @param string $post_type Post type name.
257 * @param PLL_Language $language The language of the new translation.
258 * @return string
259 */
260 public function get_new_term_link_html( WP_Term $term, string $post_type, PLL_Language $language ): string {
261 $link = $this->get_new_term_translation_link( $term, $post_type, $language );
262 return $this->new_translation_link( $link, $language );
263 }
264
265 /**
266 * Returns the URL to create a new term translation.
267 * Returns an empty string if the current user is not allowed to create terms in the given language.
268 *
269 * @since 1.5
270 * @since 3.8 Changed first parameter type from `int` to `WP_Term`.
271 * Removed 2nd parameter `$taxonomy`.
272 *
273 * @param WP_Term $term The source term.
274 * @param string $post_type Post type name.
275 * @param PLL_Language $language The language of the new translation.
276 * @return string
277 */
278 public function get_new_term_translation_link( WP_Term $term, string $post_type, PLL_Language $language ): string {
279 if ( ! $this->user->can_translate( $language ) ) {
280 return '';
281 }
282
283 $tax = get_taxonomy( $term->taxonomy );
284 if ( ! $tax || ! $this->user->has_cap( $tax->cap->edit_terms ) ) {
285 return '';
286 }
287
288 $args = array(
289 'taxonomy' => $term->taxonomy,
290 'post_type' => $post_type,
291 'from_tag' => $term->term_id,
292 'new_lang' => $language->slug,
293 );
294
295 $link = add_query_arg( $args, admin_url( 'edit-tags.php' ) );
296
297 /**
298 * Filters the new term translation link.
299 *
300 * @since 1.8
301 *
302 * @param string $link The new term translation link.
303 * @param PLL_Language $language The language of the new translation.
304 * @param int $term_id The source term id.
305 * @param string $taxonomy Taxonomy name.
306 * @param string $post_type Post type name.
307 */
308 return apply_filters( 'pll_get_new_term_translation_link', $link, $language, $term->term_id, $term->taxonomy, $post_type );
309 }
310
311 /**
312 * Returns the html markup for a term translation link.
313 *
314 * @since 3.8
315 *
316 * @param WP_Term $term The term.
317 * @param string $post_type Post type name.
318 * @param string $mode Optional. How the link should be displayed: with a pen icon or a language's flag.
319 * Possible values are:
320 * - `metabox_translation` (pen icon in metabox),
321 * - `list_translation` (pen icon in items list),
322 * - `list_current` (flag in items list).
323 * Default is `metabox_translation`.
324 * @return string
325 *
326 * @phpstan-param 'metabox_translation'|'list_translation'|'list_current' $mode
327 */
328 public function get_edit_term_link_html( WP_Term $term, string $post_type, string $mode = 'metabox_translation' ): string {
329 $language = $this->model->term->get_language( $term->term_id );
330
331 if ( empty( $language ) ) {
332 return '';
333 }
334
335 $url = (string) get_edit_term_link( $term->term_id, $term->taxonomy, $post_type );
336 return $this->get_edit_item_link_html( $url, $language, $term->term_id, $term->name, $mode );
337 }
338
339 /**
340 * Returns the language flag or the language slug if there is no flag.
341 *
342 * @since 3.8
343 *
344 * @param PLL_Language $language PLL_Language object.
345 * @return string
346 */
347 public function get_flag_html( PLL_Language $language ): string {
348 return $language->flag ?: sprintf( '<abbr>%s</abbr>', esc_html( $language->slug ) );
349 }
350
351 /**
352 * Returns some data (`from_post` and `new_lang`) from the current request.
353 *
354 * @since 3.7
355 * @since 3.8 Removed parameter.
356 *
357 * @return array {
358 * @type WP_Post $from_post The source post.
359 * @type PLL_Language $new_lang The target language.
360 * }
361 *
362 * @phpstan-return array{}|array{from_post: WP_Post, new_lang: PLL_Language}|never
363 */
364 public function get_data_from_new_post_translation_request(): array {
365 if ( isset( $_GET['from_media'] ) ) {
366 return $this->get_data_from_new_media_translation_request();
367 }
368
369 if ( ! isset( $GLOBALS['pagenow'], $GLOBALS['post_type'], $_GET['_wpnonce'], $_GET['from_post'], $_GET['new_lang'], $_GET['post_type'] ) ) {
370 return array();
371 }
372
373 if ( 'post-new.php' !== $GLOBALS['pagenow'] || ! $this->model->is_translated_post_type( $GLOBALS['post_type'] ) ) {
374 return array();
375 }
376
377 // Capability check already done in post-new.php.
378 check_admin_referer( 'new-post-translation' );
379 return $this->get_objects_from_new_post_translation_request( (int) $_GET['from_post'], sanitize_key( $_GET['new_lang'] ) );
380 }
381
382 /**
383 * Returns some data (`from_post` and `new_lang`) from the current request.
384 *
385 * @since 3.7
386 *
387 * @return array {
388 * @type WP_Post $from_post The source media.
389 * @type PLL_Language $new_lang The target language.
390 * }
391 *
392 * @phpstan-return array{}|array{from_post: WP_Post, new_lang: PLL_Language}|never
393 */
394 public function get_data_from_new_media_translation_request(): array {
395 if ( ! $this->options['media_support'] ) {
396 return array();
397 }
398
399 if ( ! isset( $_GET['action'], $_GET['_wpnonce'], $_GET['from_media'], $_GET['new_lang'] ) || 'translate_media' !== $_GET['action'] ) {
400 return array();
401 }
402
403 check_admin_referer( 'translate_media' );
404 return $this->get_objects_from_new_post_translation_request( (int) $_GET['from_media'], sanitize_key( $_GET['new_lang'] ) );
405 }
406
407 /**
408 * Returns the objects given the post ID and language slug provided in the new post translation request.
409 *
410 * @since 3.7
411 *
412 * @param int $post_id The original Post ID provided.
413 * @param string $lang_slug The new translation language provided
414 * @return array {
415 * @type WP_Post $from_post The source post.
416 * @type PLL_Language $new_lang The target language.
417 * }
418 *
419 * @phpstan-return array{}|array{from_post: WP_Post, new_lang: PLL_Language}|never
420 */
421 private function get_objects_from_new_post_translation_request( int $post_id, string $lang_slug ): array {
422 if ( $post_id <= 0 || empty( $lang_slug ) ) {
423 return array();
424 }
425
426 $post = get_post( $post_id );
427 $lang = $this->model->get_language( $lang_slug );
428
429 if ( empty( $post ) || empty( $lang ) ) {
430 return array();
431 }
432
433 return array(
434 'from_post' => $post,
435 'new_lang' => $lang,
436 );
437 }
438 }
439