diff --git a/src/wp-admin/network/themes.php b/src/wp-admin/network/themes.php index fbc2aa033c..1b9830ccce 100644 --- a/src/wp-admin/network/themes.php +++ b/src/wp-admin/network/themes.php @@ -29,12 +29,10 @@ $_SERVER['REQUEST_URI'] = remove_query_arg( $temp_args, $_SERVER['REQUEST_URI'] $referer = remove_query_arg( $temp_args, wp_get_referer() ); if ( $action ) { - $allowed_themes = get_site_option( 'allowedthemes' ); switch ( $action ) { case 'enable': check_admin_referer('enable-theme_' . $_GET['theme']); - $allowed_themes[ $_GET['theme'] ] = true; - update_site_option( 'allowedthemes', $allowed_themes ); + WP_Theme::network_enable_theme( $_GET['theme'] ); if ( false === strpos( $referer, '/network/themes.php' ) ) wp_redirect( network_admin_url( 'themes.php?enabled=1' ) ); else @@ -42,8 +40,7 @@ if ( $action ) { exit; case 'disable': check_admin_referer('disable-theme_' . $_GET['theme']); - unset( $allowed_themes[ $_GET['theme'] ] ); - update_site_option( 'allowedthemes', $allowed_themes ); + WP_Theme::network_disable_theme( $_GET['theme'] ); wp_safe_redirect( add_query_arg( 'disabled', '1', $referer ) ); exit; case 'enable-selected': @@ -53,9 +50,7 @@ if ( $action ) { wp_safe_redirect( add_query_arg( 'error', 'none', $referer ) ); exit; } - foreach ( (array) $themes as $theme ) - $allowed_themes[ $theme ] = true; - update_site_option( 'allowedthemes', $allowed_themes ); + WP_Theme::network_enable_theme( (array) $themes ); wp_safe_redirect( add_query_arg( 'enabled', count( $themes ), $referer ) ); exit; case 'disable-selected': @@ -65,9 +60,7 @@ if ( $action ) { wp_safe_redirect( add_query_arg( 'error', 'none', $referer ) ); exit; } - foreach ( (array) $themes as $theme ) - unset( $allowed_themes[ $theme ] ); - update_site_option( 'allowedthemes', $allowed_themes ); + WP_Theme::network_disable_theme( (array) $themes ); wp_safe_redirect( add_query_arg( 'disabled', count( $themes ), $referer ) ); exit; case 'update-selected' : diff --git a/src/wp-includes/class-wp-theme.php b/src/wp-includes/class-wp-theme.php index d98fb3149b..dee2fe7c4b 100644 --- a/src/wp-includes/class-wp-theme.php +++ b/src/wp-includes/class-wp-theme.php @@ -1315,6 +1315,62 @@ final class WP_Theme implements ArrayAccess { return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id ); } + /** + * Enable a theme for all sites on the current network. + * + * @since 4.6.0 + * + * @static + * @access public + * + * @param string|array $stylesheets Stylesheet name or array of stylesheet names. + */ + public static function network_enable_theme( $stylesheets ) { + if ( ! is_multisite() ) { + return; + } + + if ( ! is_array( $stylesheets ) ) { + $stylesheets = array( $stylesheets ); + } + + $allowed_themes = get_site_option( 'allowedthemes' ); + foreach ( $stylesheets as $stylesheet ) { + $allowed_themes[ $stylesheet ] = true; + } + + update_site_option( 'allowedthemes', $allowed_themes ); + } + + /** + * Disable a theme for all sites on the current network. + * + * @since 4.6.0 + * + * @static + * @access public + * + * @param string|array $stylesheets Stylesheet name or array of stylesheet names. + */ + public static function network_disable_theme( $stylesheets ) { + if ( ! is_multisite() ) { + return; + } + + if ( ! is_array( $stylesheets ) ) { + $stylesheets = array( $stylesheets ); + } + + $allowed_themes = get_site_option( 'allowedthemes' ); + foreach ( $stylesheets as $stylesheet ) { + if ( isset( $allowed_themes[ $stylesheet ] ) ) { + unset( $allowed_themes[ $stylesheet ] ); + } + } + + update_site_option( 'allowedthemes', $allowed_themes ); + } + /** * Sorts themes by name. * diff --git a/tests/phpunit/tests/theme/WPTheme.php b/tests/phpunit/tests/theme/WPTheme.php index e64aa2e7cf..5f01a25e11 100644 --- a/tests/phpunit/tests/theme/WPTheme.php +++ b/tests/phpunit/tests/theme/WPTheme.php @@ -139,4 +139,94 @@ class Tests_Theme_WPTheme extends WP_UnitTestCase { $this->assertFalse( $theme->get( 'Tags' ) ); $this->assertFalse( $theme->display( 'Tags' ) ); } + + + /** + * Enable a single theme on a network. + * + * @ticket 30594 + */ + function test_wp_theme_network_enable_single_theme() { + if ( ! is_multisite() ) { + $this->markTestSkipped( 'This test requires multisite' ); + } + + $theme = 'testtheme-1'; + $current_allowed_themes = get_site_option( 'allowedthemes' ); + WP_Theme::network_enable_theme( $theme ); + $new_allowed_themes = get_site_option( 'allowedthemes' ); + update_site_option( 'allowedthemes', $current_allowed_themes ); // reset previous value. + $current_allowed_themes['testtheme-1'] = true; // Add the new theme to the previous set. + + $this->assertEqualSetsWithIndex( $current_allowed_themes, $new_allowed_themes ); + } + + /** + * Enable multiple themes on a network. + * + * @ticket 30594 + */ + function test_wp_theme_network_enable_multiple_themes() { + if ( ! is_multisite() ) { + $this->markTestSkipped( 'This test requires multisite' ); + } + + $themes = array( 'testtheme-2', 'testtheme-3' ); + $current_allowed_themes = get_site_option( 'allowedthemes' ); + WP_Theme::network_enable_theme( $themes ); + $new_allowed_themes = get_site_option( 'allowedthemes' ); + update_site_option( 'allowedthemes', $current_allowed_themes ); // reset previous value. + $current_allowed_themes = array_merge( $current_allowed_themes, array( 'testtheme-2' => true, 'testtheme-3' => true ) ); + + $this->assertEqualSetsWithIndex( $current_allowed_themes, $new_allowed_themes ); + } + + /** + * Disable a single theme on a network. + * + * @ticket 30594 + */ + function test_network_disable_single_theme() { + if ( ! is_multisite() ) { + $this->markTestSkipped( 'This test requires multisite' ); + } + + $current_allowed_themes = get_site_option( 'allowedthemes' ); + + $allowed_themes = array( 'existing-1' => true, 'existing-2' => true, 'existing-3' => true ); + update_site_option( 'allowedthemes', $allowed_themes ); + + $disable_theme = 'existing-2'; + WP_Theme::network_disable_theme( $disable_theme ); + $new_allowed_themes = get_site_option( 'allowedthemes' ); + update_site_option( 'allowedthemes', $current_allowed_themes ); // reset previous value. + unset( $allowed_themes[ $disable_theme ] ); // Remove deleted theme from initial set. + + $this->assertEqualSetsWithIndex( $allowed_themes, $new_allowed_themes ); + } + + /** + * Disable multiple themes on a network. + * + * @ticket 30594 + */ + function test_network_disable_multiple_themes() { + if ( ! is_multisite() ) { + $this->markTestSkipped( 'This test requires multisite' ); + } + + $current_allowed_themes = get_site_option( 'allowedthemes' ); + + $allowed_themes = array( 'existing-4' => true, 'existing-5' => true, 'existing-6' => true ); + update_site_option( 'allowedthemes', $allowed_themes ); + + $disable_themes = array( 'existing-4', 'existing-5' ); + WP_Theme::network_disable_theme( $disable_themes ); + $new_allowed_themes = get_site_option( 'allowedthemes' ); + update_site_option( 'allowedthemes', $current_allowed_themes ); // reset previous value. + unset( $allowed_themes['existing-4'] ); + unset( $allowed_themes['existing-5'] ); + + $this->assertEqualSetsWithIndex( $allowed_themes, $new_allowed_themes ); + } }