WordPress.org

Make WordPress Core


Ignore:
Timestamp:
10/19/2016 03:19:13 AM (3 years ago)
Author:
westonruter
Message:

Customize: Introduce a new experience for discovering, installing, and previewing themes within the customizer.

Unify the theme-browsing and theme-customization experiences by introducing a comprehensive theme browser and installer directly accessible in the customizer. Replaces the customizer theme switcher with a full-screen panel for discovering/browsing and installing themes available on WordPress.org. Themes can now be installed and previewed directly in the customizer without entering the wp-admin context.

For details, see https://make.wordpress.org/core/2016/10/03/feature-proposal-a-new-experience-for-discovering-installing-and-previewing-themes-in-the-customizer/

Fixes #37661, #34843.
Props celloexpressions, folletto, westonruter, karmatosed, afercia.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-customize-manager.php

    r38810 r38813  
    295295        require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' );
    296296
     297        require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-panel.php' );
    297298        require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' );
    298299        require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' );
     
    349350        add_action( 'wp_ajax_customize_save',           array( $this, 'save' ) );
    350351        add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) );
     352        add_action( 'wp_ajax_customize-load-themes',    array( $this, 'load_themes_ajax' ) );
    351353
    352354        add_action( 'customize_register',                 array( $this, 'register_controls' ) );
     
    362364        // Export the settings to JS via the _wpCustomizeSettings variable.
    363365        add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings' ), 1000 );
     366
     367        // Add theme update notices.
     368        if ( current_user_can( 'install_themes' ) || current_user_can( 'update_themes' ) ) {
     369            require_once( ABSPATH . '/wp-admin/includes/update.php' );
     370            add_action( 'customize_controls_print_footer_scripts', 'wp_print_admin_notice_templates' );
     371        }
    364372    }
    365373
     
    25852593            $control->enqueue();
    25862594        }
     2595        if ( ! is_multisite() && ( current_user_can( 'install_themes' ) || current_user_can( 'update_themes' ) || current_user_can( 'delete_themes' ) ) ) {
     2596            wp_enqueue_script( 'updates' );
     2597        }
    25872598    }
    25882599
     
    27992810            'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),
    28002811            'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ),
     2812            'switch-themes' => wp_create_nonce( 'switch-themes' ),
    28012813        );
    28022814
     
    28722884            'documentTitleTmpl' => $this->get_document_title_template(),
    28732885            'previewableDevices' => $this->get_previewable_devices(),
     2886            'l10n' => array(
     2887                'confirmDeleteTheme' => __( 'Are you sure you want to delete this theme?' ),
     2888                /* translators: %d is the number of theme search results, which cannot consider singular vs. plural forms */
     2889                'themeSearchResults' => __( '%d themes found' ),
     2890                /* translators: %d is the number of themes being displayed, which cannot consider singular vs. plural forms */
     2891                'announceThemeCount' => __( 'Displaying %d themes' ),
     2892                'announceThemeDetails' => __( 'Showing details for theme: %s' ),
     2893            ),
    28742894        );
    28752895
     
    29752995        /* Panel, Section, and Control Types */
    29762996        $this->register_panel_type( 'WP_Customize_Panel' );
     2997        $this->register_panel_type( 'WP_Customize_Themes_Panel' );
    29772998        $this->register_section_type( 'WP_Customize_Section' );
    29782999        $this->register_section_type( 'WP_Customize_Sidebar_Section' );
     3000        $this->register_section_type( 'WP_Customize_Themes_Section' );
    29793001        $this->register_control_type( 'WP_Customize_Color_Control' );
    29803002        $this->register_control_type( 'WP_Customize_Media_Control' );
     
    29863008        $this->register_control_type( 'WP_Customize_Theme_Control' );
    29873009
    2988         /* Themes */
    2989 
    2990         $this->add_section( new WP_Customize_Themes_Section( $this, 'themes', array(
    2991             'title'      => $this->theme()->display( 'Name' ),
    2992             'capability' => 'switch_themes',
    2993             'priority'   => 0,
     3010        /* Themes (controls are loaded via ajax) */
     3011
     3012        $this->add_panel( new WP_Customize_Themes_Panel( $this, 'themes', array(
     3013            'title'       => $this->theme()->display( 'Name' ),
     3014            'description' => __( 'Once themes are installed, you can live-preview them on your site, customize them, and publish your new design. Browse available themes via the filters in this menu.' ),
     3015            'capability'  => 'switch_themes',
     3016            'priority'    => 0,
     3017        ) ) );
     3018
     3019        $this->add_section( new WP_Customize_Themes_Section( $this, 'installed_themes', array(
     3020            'title'       => __( 'Installed' ),
     3021            'text_before' => __( 'Your local site' ),
     3022            'action'      => 'installed',
     3023            'capability'  => 'switch_themes',
     3024            'panel'       => 'themes',
     3025            'priority'    => 0,
     3026        ) ) );
     3027
     3028        $this->add_section( new WP_Customize_Themes_Section( $this, 'search_themes', array(
     3029            'title'       => __( 'Search themes…' ),
     3030            'text_before' => __( 'Browse all WordPress.org themes' ),
     3031            'action'      => 'search',
     3032            'capability'  => 'install_themes',
     3033            'panel'       => 'themes',
     3034            'priority'    => 5,
     3035        ) ) );
     3036
     3037        $this->add_section( new WP_Customize_Themes_Section( $this, 'featured_themes', array(
     3038            'title'      => __( 'Featured' ),
     3039            'action'     => 'featured',
     3040            'capability' => 'install_themes',
     3041            'panel'      => 'themes',
     3042            'priority'   => 10,
     3043        ) ) );
     3044
     3045        $this->add_section( new WP_Customize_Themes_Section( $this, 'popular_themes', array(
     3046            'title'      => __( 'Popular' ),
     3047            'action'     => 'popular',
     3048            'capability' => 'install_themes',
     3049            'panel'      => 'themes',
     3050            'priority'   => 15,
     3051        ) ) );
     3052
     3053        $this->add_section( new WP_Customize_Themes_Section( $this, 'latest_themes', array(
     3054            'title'      => __( 'Latest' ),
     3055            'action'     => 'latest',
     3056            'capability' => 'install_themes',
     3057            'panel'      => 'themes',
     3058            'priority'   => 20,
     3059        ) ) );
     3060
     3061        $this->add_section( new WP_Customize_Themes_Section( $this, 'feature_filter_themes', array(
     3062            'title'      => __( 'Feature Filter' ),
     3063            'action'     => 'feature_filter',
     3064            'capability' => 'install_themes',
     3065            'panel'      => 'themes',
     3066            'priority'   => 25,
     3067        ) ) );
     3068
     3069        $this->add_section( new WP_Customize_Themes_Section( $this, 'favorites_themes', array(
     3070            'title'      => __( 'Favorites' ),
     3071            'action'     => 'favorites',
     3072            'capability' => 'install_themes',
     3073            'panel'      => 'themes',
     3074            'priority'   => 30,
    29943075        ) ) );
    29953076
     
    29983079            'capability' => 'switch_themes',
    29993080        ) ) );
    3000 
    3001         require_once( ABSPATH . 'wp-admin/includes/theme.php' );
    3002 
    3003         // Theme Controls.
    3004 
    3005         // Add a control for the active/original theme.
    3006         if ( ! $this->is_theme_active() ) {
    3007             $themes = wp_prepare_themes_for_js( array( wp_get_theme( $this->original_stylesheet ) ) );
    3008             $active_theme = current( $themes );
    3009             $active_theme['isActiveTheme'] = true;
    3010             $this->add_control( new WP_Customize_Theme_Control( $this, $active_theme['id'], array(
    3011                 'theme'    => $active_theme,
    3012                 'section'  => 'themes',
    3013                 'settings' => 'active_theme',
    3014             ) ) );
    3015         }
    3016 
    3017         $themes = wp_prepare_themes_for_js();
    3018         foreach ( $themes as $theme ) {
    3019             if ( $theme['active'] || $theme['id'] === $this->original_stylesheet ) {
    3020                 continue;
    3021             }
    3022 
    3023             $theme_id = 'theme_' . $theme['id'];
    3024             $theme['isActiveTheme'] = false;
    3025             $this->add_control( new WP_Customize_Theme_Control( $this, $theme_id, array(
    3026                 'theme'    => $theme,
    3027                 'section'  => 'themes',
    3028                 'settings' => 'active_theme',
    3029             ) ) );
    3030         }
    30313081
    30323082        /* Site Identity */
     
    33573407
    33583408    /**
     3409     * Load themes into the theme browsing/installation UI.
     3410     *
     3411     * @since 4.7.0
     3412     * @access public
     3413     */
     3414    public function load_themes_ajax() {
     3415        check_ajax_referer( 'switch-themes', 'switch-themes-nonce' );
     3416
     3417        if ( ! current_user_can( 'switch_themes' ) ) {
     3418            wp_die( -1 );
     3419        }
     3420
     3421        if ( empty( $_POST['theme_action'] ) ) {
     3422            wp_send_json_error( 'missing_theme_action' );
     3423        }
     3424
     3425        if ( 'search' === $_POST['theme_action'] && ! array_key_exists( 'search', $_POST ) ) {
     3426            wp_send_json_error( 'empty_search' );
     3427        } elseif ( 'favorites' === $_POST['theme_action'] && ! array_key_exists( 'user', $_POST ) ) {
     3428            wp_send_json_error( 'empty_user' );
     3429        } elseif ( 'feature_filter' === $_POST['theme_action'] && ! array_key_exists( 'tags', $_POST ) ) {
     3430            wp_send_json_error( 'no_features' );
     3431        }
     3432
     3433        require_once( ABSPATH . 'wp-admin/includes/theme.php' );
     3434        if ( 'installed' === $_POST['theme_action'] ) {
     3435            $themes = array( 'themes' => wp_prepare_themes_for_js() );
     3436            foreach ( $themes['themes'] as &$theme ) {
     3437                $theme['type'] = 'installed';
     3438                // Set active based on customized theme.
     3439                if ( $_POST['customized_theme'] === $theme['id'] ) {
     3440                    $theme['active'] = true;
     3441                } else {
     3442                    $theme['active'] = false;
     3443                }
     3444            }
     3445        } else {
     3446            if ( ! current_user_can( 'install_themes' ) ) {
     3447                wp_die( -1 );
     3448            }
     3449
     3450            // Arguments for all queries.
     3451            $args = array(
     3452                'per_page' => 100,
     3453                'page' => absint( $_POST['page'] ),
     3454                'fields' => array(
     3455                    'slug' => true,
     3456                    'screenshot' => true,
     3457                    'description' => true,
     3458                    'requires' => true,
     3459                    'rating' => true,
     3460                    'downloaded' => true,
     3461                    'downloadLink' => true,
     3462                    'last_updated' => true,
     3463                    'homepage' => true,
     3464                    'num_ratings' => true,
     3465                    'tags' => true,
     3466                ),
     3467            );
     3468
     3469            // Specialized handling for each query.
     3470            switch ( $_POST['theme_action'] ) {
     3471                case 'search':
     3472                    $args['search'] = wp_unslash( $_POST['search'] );
     3473                    break;
     3474                case 'favorites':
     3475                    $args['user'] = wp_unslash( $_POST['user'] );
     3476                case 'featured':
     3477                case 'popular':
     3478                    $args['browse'] = wp_unslash( $_POST['theme_action'] );
     3479                    break;
     3480                case 'latest':
     3481                    $args['browse'] = 'new';
     3482                    break;
     3483                case 'feature_filter':
     3484                    $args['tag'] = wp_unslash( $_POST['tags'] );
     3485                    break;
     3486            }
     3487
     3488            // Load themes from the .org API.
     3489            $themes = themes_api( 'query_themes', $args );
     3490            if ( is_wp_error( $themes ) ) {
     3491                wp_send_json_error();
     3492            }
     3493
     3494            // This list matches the allowed tags in wp-admin/includes/theme-install.php.
     3495            $themes_allowedtags = array('a' => array('href' => array(), 'title' => array(), 'target' => array()),
     3496                'abbr' => array('title' => array()), 'acronym' => array('title' => array()),
     3497                'code' => array(), 'pre' => array(), 'em' => array(), 'strong' => array(),
     3498                'div' => array(), 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(),
     3499                'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(),
     3500                'img' => array('src' => array(), 'class' => array(), 'alt' => array())
     3501            );
     3502
     3503            // Prepare a list of installed themes to check against before the loop.
     3504            $installed_themes = array();
     3505            $wp_themes = wp_get_themes();
     3506            foreach ( $wp_themes as $theme ) {
     3507                $installed_themes[] = $theme->get_stylesheet();
     3508            }
     3509            $update_php = network_admin_url( 'update.php?action=install-theme' );
     3510            foreach ( $themes->themes as &$theme ) {
     3511                $theme->install_url = add_query_arg( array(
     3512                    'theme'    => $theme->slug,
     3513                    '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ),
     3514                ), $update_php );
     3515
     3516                $theme->name        = wp_kses( $theme->name, $themes_allowedtags );
     3517                $theme->author      = wp_kses( $theme->author, $themes_allowedtags );
     3518                $theme->version     = wp_kses( $theme->version, $themes_allowedtags );
     3519                $theme->description = wp_kses( $theme->description, $themes_allowedtags );
     3520                $theme->tags        = implode( ', ', $theme->tags );
     3521                $theme->stars       = wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings, 'echo' => false ) );
     3522                $theme->num_ratings = number_format_i18n( $theme->num_ratings );
     3523                $theme->preview_url = set_url_scheme( $theme->preview_url );
     3524
     3525                // Handle themes that are already installed as installed themes.
     3526                if ( in_array( $theme->slug, $installed_themes, true ) ) {
     3527                    $theme->type = 'installed';
     3528                } else {
     3529                    $theme->type = $_POST['theme_action'];
     3530                }
     3531
     3532                // Set active based on customized theme.
     3533                if ( $_POST['customized_theme'] === $theme->slug ) {
     3534                    $theme->active = true;
     3535                } else {
     3536                    $theme->active = false;
     3537                }
     3538
     3539                // Map available theme properties to installed theme properties.
     3540                $theme->id           = $theme->slug;
     3541                $theme->screenshot   = array( $theme->screenshot_url );
     3542                $theme->authorAndUri = $theme->author;
     3543                unset( $theme->slug );
     3544                unset( $theme->screenshot_url );
     3545                unset( $theme->author );
     3546            } // End foreach().
     3547        } // End if().
     3548        wp_send_json_success( $themes );
     3549    }
     3550
     3551
     3552    /**
    33593553     * Callback for validating the header_textcolor value.
    33603554     *
Note: See TracChangeset for help on using the changeset viewer.