Make WordPress Core

Changeset 39241


Ignore:
Timestamp:
11/15/2016 09:02:38 AM (8 years ago)
Author:
westonruter
Message:

Customize: Allow starter content to apply in a new theme when switching from another theme containing changes.

  • Ensure that starter content can apply from theme B after previewing starter content in theme A.
  • Introduce new starter_content flag in changeset setting params which is used to capture whether a value is starter content and thus can be overridden.
  • Create changeset up-front with starter_content flags instead of waiting for AUTOSAVE_INTERVAL.
  • Eliminate instantiation of settings for widget instances in favor of directly calling sanitize_widget_js_instance. This eliminates issues with looking for widgets before they are registered.
  • Ensure that non-placeholders (inline arrays instead of string references) can be supplied in starter content.
  • Re-use auto-draft posts as starter content across theme switches.
  • Introduce starter_content param for WP_Customize_Manager::save_changeset_post() which is false except when starter content is being loaded on a fresh_site.

See #38114.
Fixes #38541.

Location:
trunk/src/wp-includes
Files:
3 edited

Legend:

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

    r39240 r39241  
    524524        }
    525525
    526         // Import theme starter content for fresh installs when landing in the customizer and no existing changeset loaded.
    527         if ( get_option( 'fresh_site' ) && 'customize.php' === $pagenow && ! $this->changeset_post_id() ) {
     526        /*
     527         * Import theme starter content for fresh installs when landing in the customizer.
     528         * Import starter content at after_setup_theme:100 so that any
     529         * add_theme_support( 'starter-content' ) calls will have been made.
     530         */
     531        if ( get_option( 'fresh_site' ) && 'customize.php' === $pagenow ) {
    528532            add_action( 'after_setup_theme', array( $this, 'import_theme_starter_content' ), 100 );
    529533        }
     
    886890
    887891    /**
    888      * Import theme starter content into post values.
     892     * Starter content setting IDs.
     893     *
     894     * @since 4.7.0
     895     * @access private
     896     * @var array
     897     */
     898    protected $starter_content_settings_ids = array();
     899
     900    /**
     901     * Import theme starter content into the customized state.
    889902     *
    890903     * @since 4.7.0
     
    896909        if ( empty( $starter_content ) ) {
    897910            $starter_content = get_theme_starter_content();
     911        }
     912
     913        $changeset_data = array();
     914        if ( $this->changeset_post_id() ) {
     915            $changeset_data = $this->get_changeset_post_data( $this->changeset_post_id() );
    898916        }
    899917
     
    933951                $setting_id = sprintf( 'widget_%s[%d]', $id_base, $max_widget_numbers[ $id_base ] );
    934952
    935                 $class = 'WP_Customize_Setting';
    936 
    937                 /** This filter is documented in wp-includes/class-wp-customize-manager.php */
    938                 $args = apply_filters( 'customize_dynamic_setting_args', false, $setting_id );
    939 
    940                 if ( false !== $args ) {
    941 
    942                     /** This filter is documented in wp-includes/class-wp-customize-manager.php */
    943                     $class = apply_filters( 'customize_dynamic_setting_class', $class, $setting_id, $args );
    944 
    945                     $setting = new $class( $this, $setting_id, $args );
    946                     $setting_value = call_user_func( $setting->sanitize_js_callback, $instance, $setting );
     953                $setting_value = $this->widgets->sanitize_widget_js_instance( $instance );
     954                if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) {
    947955                    $this->set_post_value( $setting_id, $setting_value );
    948                     $sidebar_widget_ids[] = $widget_id;
     956                    $this->starter_content_settings_ids[] = $setting_id;
    949957                }
    950             }
    951 
    952             $this->set_post_value( sprintf( 'sidebars_widgets[%s]', $sidebar_id ), $sidebar_widget_ids );
     958                $sidebar_widget_ids[] = $widget_id;
     959            }
     960
     961            $setting_id = sprintf( 'sidebars_widgets[%s]', $sidebar_id );
     962            if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) {
     963                $this->set_post_value( $setting_id, $sidebar_widget_ids );
     964                $this->starter_content_settings_ids[] = $setting_id;
     965            }
    953966        }
    954967
    955968        // Posts & pages.
    956969        if ( ! empty( $posts ) ) {
     970            $nav_menus_created_posts = array();
     971            if ( ! empty( $changeset_data['nav_menus_created_posts']['value'] ) ) {
     972                $nav_menus_created_posts = $changeset_data['nav_menus_created_posts']['value'];
     973            }
     974
     975            $existing_posts = array();
     976            if ( ! empty( $nav_menus_created_posts ) ) {
     977                $existing_posts_query = new WP_Query( array(
     978                    'post__in' => $nav_menus_created_posts,
     979                    'post_status' => 'auto-draft',
     980                    'post_type' => 'any',
     981                    'number' => -1,
     982                ) );
     983                foreach ( $existing_posts_query->posts as $existing_post ) {
     984                    $existing_posts[ $existing_post->post_type . ':' . $existing_post->post_name ] = $existing_post;
     985                }
     986            }
     987
    957988            foreach ( array_keys( $posts ) as $post_symbol ) {
     989                if ( empty( $posts[ $post_symbol ]['post_type'] ) ) {
     990                    continue;
     991                }
     992                $post_type = $posts[ $post_symbol ]['post_type'];
     993                if ( ! empty( $posts[ $post_symbol ]['post_name'] ) ) {
     994                    $post_name = $posts[ $post_symbol ]['post_name'];
     995                } elseif ( ! empty( $posts[ $post_symbol ]['post_title'] ) ) {
     996                    $post_name = sanitize_title( $posts[ $post_symbol ]['post_title'] );
     997                } else {
     998                    continue;
     999                }
     1000
     1001                // Use existing auto-draft post if one already exists with the same type and name.
     1002                if ( isset( $existing_posts[ $post_type . ':' . $post_name ] ) ) {
     1003                    $posts[ $post_symbol ]['ID'] = $existing_posts[ $post_type . ':' . $post_name ]->ID;
     1004                    continue;
     1005                }
     1006
    9581007                $r = $this->nav_menus->insert_auto_draft_post( $posts[ $post_symbol ] );
    9591008                if ( $r instanceof WP_Post ) {
     
    9611010                }
    9621011            }
    963             $this->set_post_value( 'nav_menus_created_posts', wp_list_pluck( $posts, 'ID' ) ); // This is why nav_menus component is dependency for adding posts.
     1012
     1013            // The nav_menus_created_posts setting is why nav_menus component is dependency for adding posts.
     1014            $setting_id = 'nav_menus_created_posts';
     1015            if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) {
     1016                $nav_menus_created_posts = array_unique( array_merge( $nav_menus_created_posts, wp_list_pluck( $posts, 'ID' ) ) );
     1017                $this->set_post_value( $setting_id, array_values( $nav_menus_created_posts ) );
     1018                $this->starter_content_settings_ids[] = $setting_id;
     1019            }
    9641020        }
    9651021
    9661022        // Nav menus.
    9671023        $placeholder_id = -1;
     1024        $reused_nav_menu_setting_ids = array();
    9681025        foreach ( $nav_menus as $nav_menu_location => $nav_menu ) {
    969             $nav_menu_term_id = $placeholder_id--;
    970             $nav_menu_setting_id = sprintf( 'nav_menu[%d]', $nav_menu_term_id );
     1026
     1027            $nav_menu_term_id = null;
     1028            $nav_menu_setting_id = null;
     1029            $matches = array();
     1030
     1031            // Look for an existing placeholder menu with starter content to re-use.
     1032            foreach ( $changeset_data as $setting_id => $setting_params ) {
     1033                $can_reuse = (
     1034                    ! empty( $setting_params['starter_content'] )
     1035                    &&
     1036                    ! in_array( $setting_id, $reused_nav_menu_setting_ids, true )
     1037                    &&
     1038                    preg_match( '#^nav_menu\[(?P<nav_menu_id>-?\d+)\]$#', $setting_id, $matches )
     1039                );
     1040                if ( $can_reuse ) {
     1041                    $nav_menu_term_id = intval( $matches['nav_menu_id'] );
     1042                    $nav_menu_setting_id = $setting_id;
     1043                    $reused_nav_menu_setting_ids[] = $setting_id;
     1044                    break;
     1045                }
     1046            }
     1047
     1048            if ( ! $nav_menu_term_id ) {
     1049                while ( isset( $changeset_data[ sprintf( 'nav_menu[%d]', $placeholder_id ) ] ) ) {
     1050                    $placeholder_id--;
     1051                }
     1052                $nav_menu_term_id = $placeholder_id;
     1053                $nav_menu_setting_id = sprintf( 'nav_menu[%d]', $placeholder_id );
     1054            }
     1055
    9711056            $this->set_post_value( $nav_menu_setting_id, array(
    9721057                'name' => isset( $nav_menu['name'] ) ? $nav_menu['name'] : $nav_menu_location,
    9731058            ) );
     1059            $this->starter_content_settings_ids[] = $nav_menu_setting_id;
    9741060
    9751061            // @todo Add support for menu_item_parent.
     
    9951081                    $nav_menu_item['object_id'] = 0;
    9961082                }
    997                 $this->set_post_value( $nav_menu_item_setting_id, $nav_menu_item );
    998             }
    999 
    1000             $this->set_post_value( sprintf( 'nav_menu_locations[%s]', $nav_menu_location ), $nav_menu_term_id );
     1083
     1084                if ( empty( $changeset_data[ $nav_menu_item_setting_id ] ) || ! empty( $changeset_data[ $nav_menu_item_setting_id ]['starter_content'] ) ) {
     1085                    $this->set_post_value( $nav_menu_item_setting_id, $nav_menu_item );
     1086                    $this->starter_content_settings_ids[] = $nav_menu_item_setting_id;
     1087                }
     1088            }
     1089
     1090            $setting_id = sprintf( 'nav_menu_locations[%s]', $nav_menu_location );
     1091            if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) {
     1092                $this->set_post_value( $setting_id, $nav_menu_term_id );
     1093                $this->starter_content_settings_ids[] = $setting_id;
     1094            }
    10011095        }
    10021096
     
    10061100                $value = $posts[ $matches['symbol'] ]['ID'];
    10071101            }
    1008             $this->set_post_value( $name, $value );
     1102
     1103            if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) {
     1104                $this->set_post_value( $name, $value );
     1105                $this->starter_content_settings_ids[] = $name;
     1106            }
    10091107        }
    10101108
     
    10141112                $value = $posts[ $matches['symbol'] ]['ID'];
    10151113            }
    1016             $this->set_post_value( $name, $value );
    1017         }
     1114
     1115            if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) {
     1116                $this->set_post_value( $name, $value );
     1117                $this->starter_content_settings_ids[] = $name;
     1118            }
     1119        }
     1120
     1121        if ( ! empty( $this->starter_content_settings_ids ) ) {
     1122            if ( did_action( 'customize_register' ) ) {
     1123                $this->_save_starter_content_changeset();
     1124            } else {
     1125                add_action( 'customize_register', array( $this, '_save_starter_content_changeset' ), 1000 );
     1126            }
     1127        }
     1128    }
     1129
     1130    /**
     1131     * Save starter content changeset.
     1132     *
     1133     * @since 4.7.0
     1134     * @access private
     1135     */
     1136    public function _save_starter_content_changeset() {
     1137
     1138        if ( empty( $this->starter_content_settings_ids ) ) {
     1139            return;
     1140        }
     1141
     1142        $this->save_changeset_post( array(
     1143            'data' => array_fill_keys( $this->starter_content_settings_ids, array( 'starter_content' => true ) ),
     1144            'starter_content' => true,
     1145        ) );
    10181146    }
    10191147
     
    18241952     *     Args for changeset post.
    18251953     *
    1826      *     @type array  $data     Optional additional changeset data. Values will be merged on top of any existing post values.
    1827      *     @type string $status   Post status. Optional. If supplied, the save will be transactional and a post revision will be allowed.
    1828      *     @type string $title    Post title. Optional.
    1829      *     @type string $date_gmt Date in GMT. Optional.
    1830      *     @type int    $user_id  ID for user who is saving the changeset. Optional, defaults to the current user ID.
     1954     *     @type array  $data            Optional additional changeset data. Values will be merged on top of any existing post values.
     1955     *     @type string $status          Post status. Optional. If supplied, the save will be transactional and a post revision will be allowed.
     1956     *     @type string $title           Post title. Optional.
     1957     *     @type string $date_gmt        Date in GMT. Optional.
     1958     *     @type int    $user_id         ID for user who is saving the changeset. Optional, defaults to the current user ID.
     1959     *     @type bool   $starter_content Whether the data is starter content. If false (default), then $starter_content will be cleared for any $data being saved.
    18311960     * }
    18321961     *
     
    18421971                'date_gmt' => null,
    18431972                'user_id' => get_current_user_id(),
     1973                'starter_content' => false,
    18441974            ),
    18451975            $args
     
    19782108                    $data[ $changeset_setting_id ] = array();
    19792109                }
     2110
    19802111                $data[ $changeset_setting_id ] = array_merge(
    19812112                    $data[ $changeset_setting_id ],
     
    19862117                    )
    19872118                );
     2119
     2120                // Clear starter_content flag in data if changeset is not explicitly being updated for starter content.
     2121                if ( empty( $args['starter_content'] ) ) {
     2122                    unset( $data[ $changeset_setting_id ]['starter_content'] );
     2123                }
    19882124            }
    19892125        }
  • trunk/src/wp-includes/class-wp-customize-nav-menus.php

    r39138 r39241  
    697697        $this->manager->add_setting( new WP_Customize_Filter_Setting( $this->manager, 'nav_menus_created_posts', array(
    698698            'transport' => 'postMessage',
     699            'type' => 'option', // To prevent theme prefix in changeset.
    699700            'default' => array(),
    700701            'sanitize_callback' => array( $this, 'sanitize_nav_menus_created_posts' ),
  • trunk/src/wp-includes/theme.php

    r39240 r39241  
    18251825    }
    18261826
    1827     $core_content = array (
     1827    $core_content = array(
    18281828        'widgets' => array(
    1829             'text_business_info' => array ( 'text', array (
     1829            'text_business_info' => array( 'text', array(
    18301830                'title' => _x( 'Find Us', 'Theme starter content' ),
    1831                 'text' => join( '', array (
     1831                'text' => join( '', array(
    18321832                    '<p><strong>' . _x( 'Address', 'Theme starter content' ) . '</strong><br />',
    18331833                    _x( '123 Main Street', 'Theme starter content' ) . '<br />' . _x( 'New York, NY 10001', 'Theme starter content' ) . '</p>',
     
    18361836                ) ),
    18371837            ) ),
    1838             'search' => array ( 'search', array (
     1838            'search' => array( 'search', array(
    18391839                'title' => _x( 'Site Search', 'Theme starter content' ),
    18401840            ) ),
    1841             'text_credits' => array ( 'text', array (
     1841            'text_credits' => array( 'text', array(
    18421842                'title' => _x( 'Site Credits', 'Theme starter content' ),
    18431843                'text' => sprintf( _x( 'This site was created on %s', 'Theme starter content' ), get_date_from_gmt( current_time( 'mysql', 1 ), 'c' ) ),
    18441844            ) ),
    18451845        ),
    1846         'nav_menus' => array (
     1846        'nav_menus' => array(
    18471847            'page_home' => array(
    18481848                'type' => 'post_type',
     
    19201920    foreach ( $config as $type => $args ) {
    19211921        switch( $type ) {
    1922             // Use options and theme_mods as-is
     1922            // Use options and theme_mods as-is.
    19231923            case 'options' :
    19241924            case 'theme_mods' :
     
    19261926                break;
    19271927
    1928             // Widgets are an extra level down due to groupings
     1928            // Widgets are grouped into sidebars.
    19291929            case 'widgets' :
    1930                 foreach ( $config[ $type ] as $group => $items ) {
    1931                     foreach ( $items as $id ) {
    1932                         if ( ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $id ] ) ) {
    1933                             $content[ $type ][ $group ][ $id ] = $core_content[ $type ][ $id ];
     1930                foreach ( $config[ $type ] as $sidebar_id => $widgets ) {
     1931                    foreach ( $widgets as $widget ) {
     1932                        if ( is_array( $widget ) ) {
     1933                            $content[ $type ][ $sidebar_id ][] = $widget;
     1934                        } elseif ( is_string( $widget ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $widget ] ) ) {
     1935                            $content[ $type ][ $sidebar_id ][] = $core_content[ $type ][ $widget ];
    19341936                        }
    19351937                    }
     
    19371939                break;
    19381940
    1939             // And nav menus are yet another level down
     1941            // And nav menu items are grouped into nav menus.
    19401942            case 'nav_menus' :
    1941                 foreach ( $config[ $type ] as $group => $args2 ) {
    1942                     // Menu groups need a name
    1943                     if ( empty( $args['name'] ) ) {
    1944                         $args2['name'] = $group;
     1943                foreach ( $config[ $type ] as $nav_menu_location => $nav_menu ) {
     1944
     1945                    // Ensure nav menus get a name.
     1946                    if ( empty( $nav_menu['name'] ) ) {
     1947                        $nav_menu['name'] = $nav_menu_location;
    19451948                    }
    19461949
    1947                     $content[ $type ][ $group ]['name'] = $args2['name'];
    1948 
    1949                     // Do we need to check if this is empty?
    1950                     foreach ( $args2['items'] as $id ) {
    1951                         if ( ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $id ] ) ) {
    1952                             $content[ $type ][ $group ]['items'][ $id ] = $core_content[ $type ][ $id ];
     1950                    $content[ $type ][ $nav_menu_location ]['name'] = $nav_menu['name'];
     1951
     1952                    foreach ( $nav_menu['items'] as $nav_menu_item ) {
     1953                        if ( is_array( $nav_menu_item ) ) {
     1954                            $content[ $type ][ $nav_menu_location ]['items'][] = $nav_menu_item;
     1955                        } elseif ( is_string( $nav_menu_item ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $nav_menu_item ] ) ) {
     1956                            $content[ $type ][ $nav_menu_location ]['items'][] = $core_content[ $type ][ $nav_menu_item ];
    19531957                        }
    19541958                    }
     
    19561960                break;
    19571961
    1958 
    1959             // Everything else should map at the next level
     1962            // Everything else should map at the next level.
    19601963            default :
    1961                 foreach( $config[ $type ] as $id ) {
    1962                     if ( ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $id ] ) ) {
    1963                         $content[ $type ][ $id ] = $core_content[ $type ][ $id ];
     1964                foreach( $config[ $type ] as $i => $item ) {
     1965                    if ( is_array( $item ) ) {
     1966                        $content[ $type ][ $i ] = $item;
     1967                    } elseif ( is_string( $item ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $item ] ) ) {
     1968                        $content[ $type ][ $item ] = $core_content[ $type ][ $item ];
    19641969                    }
    19651970                }
Note: See TracChangeset for help on using the changeset viewer.