PluginProbe
Polylang / 3.8.8
Polylang v3.8.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-base.php

admin-base.php in Polylang 3.8.8, at src/admin/admin-base.php

699 lines 22.8 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 * Setup features available on all admin pages.
10 *
11 * @since 1.8
12 */
13 abstract class PLL_Admin_Base extends PLL_Base {
14 /**
15 * @since 3.8
16 */
17 public const SCREEN_PREFIX = 'languages';
18
19 /**
20 * Current language (used to filter the content).
21 *
22 * @var PLL_Language|null
23 */
24 public $curlang;
25
26 /**
27 * Language selected in the admin language filter.
28 *
29 * @var PLL_Language|null
30 */
31 public $filter_lang;
32
33 /**
34 * Preferred language to assign to new contents.
35 *
36 * @var PLL_Language|null
37 */
38 public $pref_lang;
39
40 /**
41 * @var PLL_Filters_Links|null
42 */
43 public $filters_links;
44
45 /**
46 * @var PLL_Admin_Links|null
47 */
48 public $links;
49
50 /**
51 * @var PLL_Admin_Notices|null
52 */
53 public $notices;
54
55 /**
56 * @var PLL_Admin_Static_Pages|null
57 */
58 public $static_pages;
59
60 /**
61 * @var PLL_Admin_Default_Term|null
62 */
63 public $default_term;
64
65 /**
66 * Setups actions needed on all admin pages.
67 *
68 * @since 1.8
69 *
70 * @param PLL_Links_Model $links_model Reference to the links model.
71 */
72 public function __construct( &$links_model ) {
73 parent::__construct( $links_model );
74
75 // Adds the link to the languages panel in the WordPress admin menu
76 add_action( 'admin_menu', array( $this, 'add_menus' ) );
77
78 add_action( 'admin_menu', array( $this, 'remove_customize_submenu' ) );
79
80 // Setup js scripts and css styles
81 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ), 0 ); // High priority in case an ajax request is sent by an immediately invoked function
82
83 add_action( 'customize_controls_enqueue_scripts', array( $this, 'customize_controls_enqueue_scripts' ) );
84
85 // Early instantiated to be able to correctly initialize language properties.
86 $this->static_pages = new PLL_Admin_Static_Pages( $this );
87 $this->model->set_languages_ready();
88 }
89
90 /**
91 * Setups filters and action needed on all admin pages and on plugins page
92 * Loads the settings pages or the filters base on the request
93 *
94 * @since 1.2
95 */
96 public function init() {
97 parent::init();
98
99 $this->notices = new PLL_Admin_Notices( $this );
100
101 $this->default_term = new PLL_Admin_Default_Term( $this );
102 $this->default_term->add_hooks();
103
104 if ( ! $this->model->has_languages() ) {
105 return;
106 }
107
108 $this->links = new PLL_Admin_Links( $this ); // FIXME needed here ?
109 $this->filters_links = new PLL_Filters_Links( $this ); // FIXME needed here ?
110
111 // Filter admin language for users
112 // We must not call user info before WordPress defines user roles in wp-settings.php
113 add_action( 'setup_theme', array( $this, 'init_user' ) );
114 add_filter( 'request', array( $this, 'request' ) );
115
116 // Adds the languages in admin bar
117 add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ), 100 ); // 100 determines the position
118 }
119
120 /**
121 * Adds links to Polylang's admin panels to the WordPress admin menu.
122 *
123 * @since 0.1
124 *
125 * @return void
126 */
127 public function add_menus(): void {
128 global $admin_page_hooks;
129
130 $parent = '';
131 $first_tab = '';
132
133 foreach ( $this->get_menu_items() as $tab => $title ) {
134 $page = self::get_screen_slug( $tab );
135 $capa = $this->get_menu_capability( $tab );
136
137 if ( empty( $parent ) ) {
138 $parent = $page;
139 $first_tab = $tab;
140
141 /*
142 * WP actually doesn't care about the user capability used here, as long as it has sub-menus: it will
143 * use the ones from the sub-menus. See `_wp_menu_output()`.
144 * Ex: a user with `manage_translations` will still be able to access the Translations page, even if the
145 * main menu has `manage_options`.
146 */
147 add_menu_page( $title, __( 'Languages', 'polylang' ), $capa, $parent, '__return_null', 'dashicons-translation' );
148 $admin_page_hooks[ $parent ] = self::SCREEN_PREFIX; // Hack to avoid the localization of the hook name. See: https://core.trac.wordpress.org/ticket/18857
149 }
150
151 add_submenu_page( $parent, $title, $title, $capa, $page, array( $this, 'languages_page' ) );
152 }
153
154 /*
155 * Get rid of the `toplevel` prefix in hook names.
156 *
157 * In the WP admin, if an admin screen is the first of its menu (like the PLL's "Languages" screen), the hooks
158 * fired in the screen get a `toplevel` prefix (ex: `toplevel_page_mlang`) while all the other screens get a
159 * slug based on the parent screen title (ex: `languages_page_mlang_strings`, where `languages` is the parent
160 * screen's slug). This will not prevent the `toplevel` hooks to fire, but it will fire the `languages` hooks in
161 * addition: this way, screens can be removed or moved around without the need of hooking both prefixes: using
162 * the hooks with the `languages` prefix will work in both cases.
163 *
164 * @see get_plugin_page_hookname()
165 */
166 foreach ( array( 'load-', 'admin_print_styles-', 'admin_print_scripts-', 'admin_head-', '', 'admin_print_footer_scripts-', 'admin_footer-' ) as $prefix ) {
167 add_action(
168 "{$prefix}toplevel_page_{$parent}",
169 static function () use ( $prefix, $first_tab ) {
170 do_action( $prefix . self::get_screen_id( $first_tab ) );
171 }
172 );
173 }
174
175 /*
176 * Ensure a common CSS class to the `<body>` tag.
177 *
178 * Due to the `toplevel` "issue" described earlier, the CSS class `toplevel_page_mlang` (for example) is added
179 * to the body. This adds a class with the `languages` prefix. This ensures we have a common CSS class, even if
180 * the screen is moved to the 1st position in the menu.
181 */
182 add_action(
183 // Target the screen in 1st position only.
184 "admin_head-toplevel_page_{$parent}",
185 static function () use ( $first_tab ) {
186 add_filter(
187 'admin_body_class',
188 static function ( $admin_body_classes ) use ( $first_tab ) {
189 return $admin_body_classes . ' ' . self::get_screen_id( $first_tab );
190 }
191 );
192 }
193 );
194
195 /**
196 * Also modify the screen ID and base.
197 *
198 * Note: the global variables `$page_hook` and `$hook_suffix` are not changed, their value is still
199 * `toplevel_page_mlang`. Changing them breaks things because we can't filter `get_plugin_page_hookname()`.
200 * This is why the above hooks are still needed.
201 */
202 add_action(
203 'current_screen',
204 static function ( $current_screen ) use ( $parent, $first_tab ) {
205 if ( "toplevel_page_{$parent}" !== $current_screen->id ) {
206 return;
207 }
208
209 $current_screen->id = self::get_screen_id( $first_tab );
210 $current_screen->base = self::get_screen_id( $first_tab );
211 }
212 );
213 }
214
215 /**
216 * Dummy method to display the 3 tabs pages: languages, strings translations, settings.
217 * Overwritten in `PLL_Settings`.
218 *
219 * @since 3.7
220 *
221 * @return void
222 */
223 public function languages_page() {}
224
225 /**
226 * Setup js scripts & css styles ( only on the relevant pages )
227 *
228 * @since 0.6
229 *
230 * @return void
231 */
232 public function admin_enqueue_scripts() {
233 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
234
235 wp_enqueue_script( 'pll_admin', plugins_url( "/js/build/admin{$suffix}.js", POLYLANG_ROOT_FILE ), array( 'jquery' ), POLYLANG_VERSION, true );
236 $inline_script = sprintf( 'let pll_admin = %s;', wp_json_encode( array( 'ajax_filter' => $this->get_ajax_filter_data() ) ) );
237 wp_add_inline_script( 'pll_admin', $inline_script, 'before' );
238
239 $screen = get_current_screen();
240 if ( empty( $screen ) ) {
241 return;
242 }
243
244 /*
245 * For each script:
246 * 0 => the pages on which to load the script
247 * 1 => the scripts it needs to work
248 * 2 => true if loaded even if languages have not been defined yet, false otherwise
249 * 3 => true if loaded in footer
250 */
251 $scripts = array(
252 'widgets' => array( array( 'widgets' ), array( 'jquery' ), false, false ),
253 );
254
255 $block_screens = array( 'widgets', 'site-editor' );
256
257 if ( ! empty( $screen->post_type ) && $this->model->is_translated_post_type( $screen->post_type ) ) {
258 $scripts['post'] = array( array( 'edit' ), array( 'jquery', 'wp-ajax-response' ), false, true );
259
260 // Classic editor.
261 if ( ! method_exists( $screen, 'is_block_editor' ) || ! $screen->is_block_editor() ) {
262 $scripts['classic-editor'] = array( array( 'post', 'media', 'async-upload' ), array( 'jquery', 'wp-ajax-response', 'post', 'jquery-ui-dialog', 'wp-i18n' ), false, true );
263 }
264
265 // Block editor with legacy metabox in WP 5.0+.
266 $block_screens[] = 'post';
267 }
268
269 if ( $this->options['media_support'] ) {
270 $scripts['media'] = array( array( 'upload' ), array( 'jquery' ), false, true );
271 }
272
273 if ( $this->is_block_editor( $screen ) ) {
274 $scripts['block-editor'] = array( $block_screens, array( 'jquery', 'wp-ajax-response', 'wp-api-fetch', 'jquery-ui-dialog', 'wp-i18n' ), false, true );
275 }
276
277 if ( ! empty( $screen->taxonomy ) && $this->model->is_translated_taxonomy( $screen->taxonomy ) ) {
278 $scripts['term'] = array( array( 'edit-tags', 'term' ), array( 'jquery', 'wp-ajax-response', 'jquery-ui-autocomplete' ), false, true );
279 }
280
281 foreach ( $scripts as $script => $v ) {
282 if ( in_array( $screen->base, $v[0] ) && ( $v[2] || $this->model->has_languages() ) ) {
283 wp_enqueue_script( "pll_{$script}", plugins_url( "/js/build/{$script}{$suffix}.js", POLYLANG_ROOT_FILE ), $v[1], POLYLANG_VERSION, $v[3] );
284 if ( 'classic-editor' === $script || 'block-editor' === $script ) {
285 wp_set_script_translations( "pll_{$script}", 'polylang' );
286 }
287 }
288 }
289
290 wp_register_style( 'polylang_admin', plugins_url( "/css/build/admin{$suffix}.css", POLYLANG_ROOT_FILE ), array( 'wp-jquery-ui-dialog' ), POLYLANG_VERSION );
291 wp_enqueue_style( 'polylang_dialog', plugins_url( "/css/build/dialog{$suffix}.css", POLYLANG_ROOT_FILE ), array( 'polylang_admin' ), POLYLANG_VERSION );
292
293 $this->add_inline_scripts();
294 }
295
296 /**
297 * Tells whether or not the given screen is block editor kind.
298 * e.g. widget, site or post editor.
299 *
300 * @since 3.3
301 *
302 * @param WP_Screen $screen Screen object.
303 * @return bool True if the screen is a block editor, false otherwise.
304 */
305 protected function is_block_editor( $screen ) {
306 return method_exists( $screen, 'is_block_editor' ) && $screen->is_block_editor() && ! pll_use_block_editor_plugin();
307 }
308
309 /**
310 * Enqueue scripts to the WP Customizer.
311 *
312 * @since 2.4.0
313 *
314 * @return void
315 */
316 public function customize_controls_enqueue_scripts() {
317 if ( $this->model->has_languages() ) {
318 $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
319 wp_enqueue_script( 'pll_widgets', plugins_url( '/js/build/widgets' . $suffix . '.js', POLYLANG_ROOT_FILE ), array( 'jquery' ), POLYLANG_VERSION, true );
320 $this->add_inline_scripts();
321 }
322 }
323
324 /**
325 * Adds inline scripts to set the default language in JS
326 * and localizes scripts.
327 *
328 * @since 3.3
329 *
330 * @return void
331 */
332 private function add_inline_scripts() {
333 if ( wp_script_is( 'pll_block-editor', 'enqueued' ) ) {
334 $default_lang_script = sprintf( 'const pllDefaultLanguage = %s;', wp_json_encode( $this->options['default_lang'] ) );
335 wp_add_inline_script(
336 'pll_block-editor',
337 $default_lang_script,
338 'before'
339 );
340 }
341 if ( wp_script_is( 'pll_widgets', 'enqueued' ) ) {
342 wp_localize_script(
343 'pll_widgets',
344 'pll_widgets',
345 array(
346 'flags' => wp_list_pluck( $this->model->get_languages_list(), 'flag', 'slug' ),
347 )
348 );
349 }
350 }
351
352 /**
353 * Returns the data to use with the AJAX filter.
354 * The final goal is to detect if an ajax request is made on admin or frontend.
355 *
356 * Takes care to various situations:
357 * - When the AJAX request has no `options.data` thanks to ScreenfeedFr.
358 * See: https://wordpress.org/support/topic/ajaxprefilter-may-not-work-as-expected.
359 * - When `options.data` is a JSON string.
360 * See: https://wordpress.org/support/topic/polylang-breaking-third-party-ajax-requests-on-admin-panels.
361 * - When `options.data` is an empty string (GET request with the method 'load').
362 * See: https://wordpress.org/support/topic/invalid-url-during-wordpress-new-dashboard-widget-operation.
363 *
364 * @since 3.7
365 *
366 * @return array
367 */
368 public function get_ajax_filter_data(): array {
369 global $post, $tag;
370
371 $params = array( 'pll_ajax_backend' => 1 );
372 if ( $post instanceof WP_Post && $this->model->post_types->is_translated( $post->post_type ) ) {
373 $params['pll_post_id'] = $post->ID;
374 }
375
376 if ( $tag instanceof WP_Term && $this->model->taxonomies->is_translated( $tag->taxonomy ) ) {
377 $params['pll_term_id'] = $tag->term_id;
378 }
379
380 /**
381 * Filters the list of parameters to add to the admin ajax request.
382 *
383 * @since 3.4.5
384 *
385 * @param array $params List of parameters to add to the admin ajax request.
386 */
387 return (array) apply_filters( 'pll_admin_ajax_params', $params );
388 }
389
390 /**
391 * Sets the admin current language, used to filter the content
392 *
393 * @since 2.0
394 *
395 * @return void
396 */
397 public function set_current_language() {
398 $this->curlang = $this->filter_lang;
399
400 // Edit Post
401 if ( isset( $_REQUEST['pll_post_id'] ) && $lang = $this->model->post->get_language( (int) $_REQUEST['pll_post_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
402 $this->curlang = $lang;
403 } elseif ( 'post.php' === $GLOBALS['pagenow'] && isset( $_GET['post'] ) && $this->model->is_translated_post_type( get_post_type( (int) $_GET['post'] ) ) && $lang = $this->model->post->get_language( (int) $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
404 $this->curlang = $lang;
405 } elseif ( 'post-new.php' === $GLOBALS['pagenow'] && ( empty( $_GET['post_type'] ) || $this->model->is_translated_post_type( sanitize_key( $_GET['post_type'] ) ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
406 $this->curlang = empty( $_GET['new_lang'] ) ? $this->pref_lang : $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
407 }
408
409 // Edit Term
410 elseif ( isset( $_REQUEST['pll_term_id'] ) && $lang = $this->model->term->get_language( (int) $_REQUEST['pll_term_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
411 $this->curlang = $lang;
412 } elseif ( in_array( $GLOBALS['pagenow'], array( 'edit-tags.php', 'term.php' ) ) && isset( $_GET['taxonomy'] ) && $this->model->is_translated_taxonomy( sanitize_key( $_GET['taxonomy'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
413 if ( isset( $_GET['tag_ID'] ) && $lang = $this->model->term->get_language( (int) $_GET['tag_ID'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
414 $this->curlang = $lang;
415 } elseif ( ! empty( $_GET['new_lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
416 $this->curlang = $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
417 } elseif ( empty( $this->curlang ) ) {
418 $this->curlang = $this->pref_lang;
419 }
420 }
421
422 // Ajax
423 if ( wp_doing_ajax() && ! empty( $_REQUEST['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
424 $this->curlang = $this->model->get_language( sanitize_key( $_REQUEST['lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
425 }
426
427 /**
428 * Filters the current language used by Polylang in the admin context.
429 *
430 * @since 3.2
431 *
432 * @param PLL_Language|false|null $curlang Instance of the current language.
433 * @param PLL_Admin_Base $polylang Instance of the main Polylang's object.
434 */
435 $this->curlang = apply_filters( 'pll_admin_current_language', $this->curlang, $this );
436
437 // Inform that the admin language has been set.
438 if ( $this->curlang instanceof PLL_Language ) {
439 /** This action is documented in src/frontend/choose-lang.php */
440 do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang );
441 } else {
442 /** This action is documented in src/class-polylang.php */
443 do_action( 'pll_no_language_defined' ); // To load overridden textdomains.
444 }
445 }
446
447 /**
448 * Defines the backend language and the admin language filter based on user preferences.
449 *
450 * @since 1.2.3
451 *
452 * @return void
453 */
454 public function init_user() {
455 /*
456 * $_GET['lang'] is numeric when editing a language, not when selecting a new language in the filter.
457 * We intentionally don't use a nonce to update the language filter.
458 */
459 if ( ! wp_doing_ajax() && ! empty( $_GET['lang'] ) && ! is_numeric( sanitize_key( $_GET['lang'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
460 $user_id = get_current_user_id();
461 if ( current_user_can( 'edit_user', $user_id ) ) {
462 $lang = $this->model->get_language( sanitize_key( $_GET['lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
463 update_user_meta( $user_id, 'pll_filter_content', $lang ? $lang->slug : '' );
464 }
465 }
466
467 $this->filter_lang = $this->model->get_language( get_user_meta( get_current_user_id(), 'pll_filter_content', true ) );
468
469 // Set preferred language for use when saving posts and terms: must not be empty.
470 $this->pref_lang = empty( $this->filter_lang ) ? $this->model->get_default_language() : $this->filter_lang;
471
472 /**
473 * Filters the preferred language on admin side.
474 * The preferred language is used for example to determine the language of a new post.
475 *
476 * @since 1.2.3
477 *
478 * @param PLL_Language $pref_lang Preferred language.
479 */
480 $this->pref_lang = apply_filters( 'pll_admin_preferred_language', $this->pref_lang );
481
482 $this->set_current_language();
483 }
484
485 /**
486 * Avoids parsing a tax query when all languages are requested
487 * Fixes https://wordpress.org/support/topic/notice-undefined-offset-0-in-wp-includesqueryphp-on-line-3877 introduced in WP 4.1
488 *
489 * @see https://core.trac.wordpress.org/ticket/31246 the suggestion of @boonebgorges.
490 *
491 * @since 1.6.5
492 *
493 * @param array $qvars The array of requested query variables.
494 * @return array
495 */
496 public function request( $qvars ) {
497 if ( isset( $qvars['lang'] ) && 'all' === $qvars['lang'] ) {
498 unset( $qvars['lang'] );
499 }
500
501 return $qvars;
502 }
503
504 /**
505 * Adds the languages list in admin bar for the admin languages filter.
506 *
507 * @since 0.9
508 *
509 * @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar global object.
510 * @return void
511 */
512 public function admin_bar_menu( $wp_admin_bar ) {
513 $all_item = (object) array(
514 'slug' => 'all',
515 'name' => __( 'Show all languages', 'polylang' ),
516 'flag' => '<span class="ab-icon"></span>',
517 );
518
519 $selected = empty( $this->filter_lang ) ? $all_item : $this->filter_lang;
520
521 $title = sprintf(
522 '<span class="ab-label"%1$s><span class="screen-reader-text">%2$s</span>%3$s</span>',
523 $selected instanceof PLL_Language ? sprintf( ' lang="%s"', esc_attr( $selected->get_locale( 'display' ) ) ) : '',
524 __( 'Filters content by language', 'polylang' ),
525 esc_html( $selected->name )
526 );
527
528 $all_items = array_merge( array( $all_item ), $this->model->get_languages_list() );
529 $items = $all_items;
530
531 if ( $this->should_hide_admin_bar_menu() ) {
532 $items = array();
533 }
534
535 /**
536 * Filters the admin bar language filter submenu items.
537 *
538 * @since 2.6
539 * @since 3.8 Added `$all_items` parameter.
540 *
541 * @param array $items The items of the admin languages filter to display (may be empty if menu hidden).
542 * @param array $all_items Complete unfiltered list of all available language items.
543 */
544 $items = apply_filters( 'pll_admin_languages_filter', $items, $all_items );
545
546 if ( empty( $items ) ) {
547 return;
548 }
549
550 $wp_admin_bar->add_menu(
551 array(
552 'id' => 'languages',
553 'title' => $selected->flag . $title,
554 'href' => esc_url( add_query_arg( 'lang', $selected->slug, remove_query_arg( 'paged' ) ) ),
555 'meta' => array(
556 'title' => __( 'Filters content by language', 'polylang' ),
557 'class' => 'all' === $selected->slug ? '' : 'pll-filtered-languages',
558 ),
559 )
560 );
561
562 foreach ( $items as $lang ) {
563 if ( $selected->slug === $lang->slug ) {
564 continue;
565 }
566
567 $wp_admin_bar->add_menu(
568 array(
569 'parent' => 'languages',
570 'id' => $lang->slug,
571 'title' => $lang->flag . esc_html( $lang->name ),
572 'href' => esc_url( add_query_arg( 'lang', $lang->slug, remove_query_arg( 'paged' ) ) ),
573 'meta' => 'all' === $lang->slug ? array() : array( 'lang' => esc_attr( $lang->get_locale( 'display' ) ) ),
574 )
575 );
576 }
577 }
578
579 /**
580 * Remove the customize submenu when using a block theme.
581 *
582 * WordPress removes the Customizer menu if a block theme is activated and no other plugins interact with it.
583 * As Polylang interacts with the Customizer, we have to delete this menu ourselves in the case of a block theme,
584 * unless another plugin than Polylang interacts with the Customizer.
585 *
586 * @since 3.2
587 *
588 * @return void
589 */
590 public function remove_customize_submenu() {
591 if ( ! $this->should_customize_menu_be_removed() ) {
592 return;
593 }
594
595 global $submenu;
596
597 if ( ! empty( $submenu['themes.php'] ) ) {
598 foreach ( $submenu['themes.php'] as $submenu_item ) {
599 if ( 'customize' === $submenu_item[1] ) {
600 remove_submenu_page( 'themes.php', $submenu_item[2] );
601 }
602 }
603 }
604 }
605
606 /**
607 * Tells if the Polylang's admin bar menu should be hidden for the current page.
608 * Conventionally, it should be hidden on edition pages, term edit pages and Site Editor pages.
609 *
610 * @since 3.8
611 *
612 * @return bool
613 */
614 public function should_hide_admin_bar_menu(): bool {
615 global $pagenow;
616
617 return in_array( $pagenow, array( 'post.php', 'post-new.php', 'site-editor.php', 'term.php' ), true );
618 }
619
620 /**
621 * Returns the ID of a Polylang's settings screen.
622 *
623 * @since 3.8
624 *
625 * @param string $tab The name of the screen (`lang`, `strings`, `settings`).
626 * @return string
627 *
628 * @phpstan-return non-empty-string
629 */
630 public static function get_screen_id( string $tab ): string {
631 return sprintf( '%s_page_%s', self::SCREEN_PREFIX, self::get_screen_slug( $tab ) );
632 }
633
634 /**
635 * Returns the slug of a Polylang's settings screen, as seen in the URL.
636 *
637 * @since 3.8
638 *
639 * @param string $tab The name of the screen (`lang`, `strings`, `settings`).
640 * @return string
641 *
642 * @phpstan-return non-empty-string
643 */
644 public static function get_screen_slug( string $tab ): string {
645 return 'lang' === $tab ? 'mlang' : "mlang_$tab";
646 }
647
648 /**
649 * Returns the list of sub-menu items.
650 *
651 * @since 3.8
652 *
653 * @return string[] List of sub-menu items with page slugs as array keys, and sub-menu titles as array values.
654 *
655 * @phpstan-return array<non-empty-string, string>
656 */
657 protected function get_menu_items(): array {
658 $tabs = array(
659 'lang' => __( 'Languages', 'polylang' ),
660 );
661
662 // Only if at least one language has been created.
663 if ( ! empty( $this->model->languages->filter( 'translator' )->get_list() ) ) {
664 $tabs['strings'] = __( 'Translations', 'polylang' );
665 }
666
667 $tabs['settings'] = __( 'Settings', 'polylang' );
668
669 /**
670 * Filter the list of sub-menu items in Polylang settings.
671 *
672 * @since 1.5.1
673 *
674 * @param string[] $tabs List of sub-menu items with page slugs as array keys and titles as array values.
675 */
676 return (array) apply_filters( 'pll_settings_tabs', $tabs );
677 }
678
679 /**
680 * Returns the user capability required to access the given menu page.
681 *
682 * @since 3.8
683 *
684 * @param string $menu Menu slug.
685 * @return string
686 */
687 protected function get_menu_capability( string $menu ): string {
688 switch ( $menu ) {
689 case 'lang':
690 return Capabilities::LANGUAGES;
691
692 case 'strings':
693 return Capabilities::TRANSLATIONS;
694 }
695
696 return 'manage_options';
697 }
698 }
699