diff --git a/src/wp-admin/network/settings.php b/src/wp-admin/network/settings.php
index 1d527c8723..1d3e03519f 100644
a
|
b
|
if ( $_POST ) { |
63 | 63 | ); |
64 | 64 | |
65 | 65 | // Handle translation install. |
66 | | if ( ! empty( $_POST['WPLANG'] ) && wp_can_install_language_pack() ) { // @todo: Skip if already installed |
| 66 | if ( ! empty( $_POST['WPLANG'] ) && current_user_can( 'install_languages' ) ) { |
67 | 67 | $language = wp_download_language_pack( $_POST['WPLANG'] ); |
68 | 68 | if ( $language ) { |
69 | 69 | $_POST['WPLANG'] = $language; |
… |
… |
if ( isset( $_GET['updated'] ) ) { |
342 | 342 | 'selected' => $lang, |
343 | 343 | 'languages' => $languages, |
344 | 344 | 'translations' => $translations, |
345 | | 'show_available_translations' => wp_can_install_language_pack(), |
| 345 | 'show_available_translations' => current_user_can( 'install_languages' ), |
346 | 346 | ) ); |
347 | 347 | ?> |
348 | 348 | </td> |
diff --git a/src/wp-admin/network/site-new.php b/src/wp-admin/network/site-new.php
index 0810f8a87a..e0ccfc26fa 100644
a
|
b
|
if ( isset($_REQUEST['action']) && 'add-site' == $_REQUEST['action'] ) { |
66 | 66 | if ( isset( $_POST['WPLANG'] ) ) { |
67 | 67 | if ( '' === $_POST['WPLANG'] ) { |
68 | 68 | $meta['WPLANG'] = ''; // en_US |
69 | | } elseif ( wp_can_install_language_pack() ) { |
| 69 | } elseif ( in_array( $_POST['WPLANG'], get_available_languages() ) ) { |
| 70 | $meta['WPLANG'] = $_POST['WPLANG']; |
| 71 | } elseif ( current_user_can( 'install_languages' ) ) { |
70 | 72 | $language = wp_download_language_pack( wp_unslash( $_POST['WPLANG'] ) ); |
71 | 73 | if ( $language ) { |
72 | 74 | $meta['WPLANG'] = $language; |
… |
… |
if ( ! empty( $messages ) ) { |
234 | 236 | 'selected' => $lang, |
235 | 237 | 'languages' => $languages, |
236 | 238 | 'translations' => $translations, |
237 | | 'show_available_translations' => wp_can_install_language_pack(), |
| 239 | 'show_available_translations' => current_user_can( 'install_languages' ), |
238 | 240 | ) ); |
239 | 241 | ?> |
240 | 242 | </td> |
diff --git a/src/wp-admin/options-general.php b/src/wp-admin/options-general.php
index 834f02733a..a5364a6b62 100644
a
|
b
|
if ( ! empty( $languages ) || ! empty( $translations ) ) { |
144 | 144 | 'selected' => $locale, |
145 | 145 | 'languages' => $languages, |
146 | 146 | 'translations' => $translations, |
147 | | 'show_available_translations' => ( ! is_multisite() || is_super_admin() ) && wp_can_install_language_pack(), |
| 147 | 'show_available_translations' => current_user_can( 'install_languages' ), |
148 | 148 | ) ); |
149 | 149 | |
150 | 150 | // Add note about deprecated WPLANG constant. |
diff --git a/src/wp-admin/options.php b/src/wp-admin/options.php
index d2e1c0374f..bf7141c43f 100644
a
|
b
|
if ( 'update' == $action ) { |
182 | 182 | } |
183 | 183 | |
184 | 184 | // Handle translation install. |
185 | | if ( ! empty( $_POST['WPLANG'] ) && ( ! is_multisite() || is_super_admin() ) ) { // @todo: Skip if already installed |
| 185 | if ( ! empty( $_POST['WPLANG'] ) && current_user_can( 'install_languages' ) ) { |
186 | 186 | require_once( ABSPATH . 'wp-admin/includes/translation-install.php' ); |
187 | 187 | |
188 | | if ( wp_can_install_language_pack() ) { |
189 | | $language = wp_download_language_pack( $_POST['WPLANG'] ); |
190 | | if ( $language ) { |
191 | | $_POST['WPLANG'] = $language; |
192 | | } |
| 188 | $language = wp_download_language_pack( $_POST['WPLANG'] ); |
| 189 | if ( $language ) { |
| 190 | $_POST['WPLANG'] = $language; |
193 | 191 | } |
194 | 192 | } |
195 | 193 | } |
diff --git a/src/wp-admin/update-core.php b/src/wp-admin/update-core.php
index a0801b9bad..83b7be539b 100644
a
|
b
|
if ( 'upgrade-core' == $action ) { |
608 | 608 | echo ' <a class="button" href="' . esc_url( self_admin_url('update-core.php?force-check=1') ) . '">' . __( 'Check Again' ) . '</a>'; |
609 | 609 | echo '</p>'; |
610 | 610 | |
611 | | if ( $core = current_user_can( 'update_core' ) ) |
| 611 | if ( current_user_can( 'update_core' ) ) { |
612 | 612 | core_upgrade_preamble(); |
613 | | if ( $plugins = current_user_can( 'update_plugins' ) ) |
| 613 | } |
| 614 | if ( current_user_can( 'update_plugins' ) ) { |
614 | 615 | list_plugin_updates(); |
615 | | if ( $themes = current_user_can( 'update_themes' ) ) |
| 616 | } |
| 617 | if ( current_user_can( 'update_themes' ) ) { |
616 | 618 | list_theme_updates(); |
617 | | if ( $core || $plugins || $themes ) |
| 619 | } |
| 620 | if ( current_user_can( 'update_languages' ) ) { |
618 | 621 | list_translation_updates(); |
619 | | unset( $core, $plugins, $themes ); |
| 622 | } |
| 623 | |
620 | 624 | /** |
621 | 625 | * Fires after the core, plugin, and theme update tables. |
622 | 626 | * |
… |
… |
if ( 'upgrade-core' == $action ) { |
729 | 733 | |
730 | 734 | } elseif ( 'do-translation-upgrade' == $action ) { |
731 | 735 | |
732 | | if ( ! current_user_can( 'update_core' ) && ! current_user_can( 'update_plugins' ) && ! current_user_can( 'update_themes' ) ) |
| 736 | if ( ! current_user_can( 'update_languages' ) ) |
733 | 737 | wp_die( __( 'Sorry, you are not allowed to update this site.' ) ); |
734 | 738 | |
735 | 739 | check_admin_referer( 'upgrade-translations' ); |
diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php
index 13851cc2e1..5993a77caf 100644
a
|
b
|
function map_meta_cap( $cap, $user_id ) { |
392 | 392 | $caps[] = $cap; |
393 | 393 | } |
394 | 394 | break; |
| 395 | case 'install_languages': |
| 396 | case 'update_languages': |
| 397 | if ( ! function_exists( 'wp_can_install_language_pack' ) ) { |
| 398 | require_once( ABSPATH . 'wp-admin/includes/translation-install.php' ); |
| 399 | } |
| 400 | |
| 401 | if ( ! wp_can_install_language_pack() ) { |
| 402 | $caps[] = 'do_not_allow'; |
| 403 | } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) { |
| 404 | $caps[] = 'do_not_allow'; |
| 405 | } else { |
| 406 | $caps[] = 'install_languages'; |
| 407 | } |
| 408 | break; |
395 | 409 | case 'activate_plugins': |
396 | 410 | $caps[] = $cap; |
397 | 411 | if ( is_multisite() ) { |
… |
… |
function revoke_super_admin( $user_id ) { |
826 | 840 | } |
827 | 841 | return false; |
828 | 842 | } |
| 843 | |
| 844 | /** |
| 845 | * Filters the user capabilities to grant the 'install_languages' capability as necessary. |
| 846 | * |
| 847 | * A user must have at least one out of the 'update_core', 'install_plugins', and |
| 848 | * 'install_themes' capabilities to qualify for 'install_languages'. |
| 849 | * |
| 850 | * @since 4.9.0 |
| 851 | * |
| 852 | * @param array $allcaps An array of all the user's capabilities. |
| 853 | * @return array Filtered array of the user's capabilities. |
| 854 | */ |
| 855 | function wp_maybe_grant_install_languages_cap( $allcaps ) { |
| 856 | if ( ! empty( $allcaps['update_core'] ) || ! empty( $allcaps['install_plugins'] ) || ! empty( $allcaps['install_themes'] ) ) { |
| 857 | $allcaps['install_languages'] = true; |
| 858 | } |
| 859 | |
| 860 | return $allcaps; |
| 861 | } |
diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php
index 8f0237028d..72a2b7f596 100644
a
|
b
|
add_filter( 'oembed_dataparse', 'wp_filter_oembed_result', 10, 3 ); |
511 | 511 | add_filter( 'oembed_response_data', 'get_oembed_response_data_rich', 10, 4 ); |
512 | 512 | add_filter( 'pre_oembed_result', 'wp_filter_pre_oembed_result', 10, 3 ); |
513 | 513 | |
| 514 | // Capabilities |
| 515 | add_filter( 'user_has_cap', 'wp_maybe_grant_install_languages_cap', 1 ); |
| 516 | |
514 | 517 | unset( $filter, $action ); |
diff --git a/tests/phpunit/tests/user/capabilities.php b/tests/phpunit/tests/user/capabilities.php
index 2dffd25c9f..641b8d1faa 100644
a
|
b
|
class Tests_User_Capabilities extends WP_UnitTestCase { |
233 | 233 | 'upload_themes' => array( 'administrator' ), |
234 | 234 | 'customize' => array( 'administrator' ), |
235 | 235 | 'add_users' => array( 'administrator' ), |
| 236 | 'install_languages' => array( 'administrator' ), |
| 237 | 'update_languages' => array( 'administrator' ), |
236 | 238 | |
237 | 239 | 'edit_categories' => array( 'administrator', 'editor' ), |
238 | 240 | 'delete_categories' => array( 'administrator', 'editor' ), |
… |
… |
class Tests_User_Capabilities extends WP_UnitTestCase { |
261 | 263 | 'upload_themes' => array(), |
262 | 264 | 'edit_css' => array(), |
263 | 265 | 'upgrade_network' => array(), |
| 266 | 'install_languages' => array(), |
| 267 | 'update_languages' => array(), |
264 | 268 | |
265 | 269 | 'customize' => array( 'administrator' ), |
266 | 270 | 'delete_site' => array( 'administrator' ), |