WordPress.org

Make WordPress Core

Ticket #38615: 38615.13.diff

File 38615.13.diff, 23.2 KB (added by westonruter, 3 years ago)

Δ https://github.com/xwp/wordpress-develop/pull/201/files/33f04fc..34978df

  • src/wp-content/themes/twentyseventeen/functions.php

    diff --git src/wp-content/themes/twentyseventeen/functions.php src/wp-content/themes/twentyseventeen/functions.php
    index 1bcb95f..19770b3 100644
    function twentyseventeen_setup() { 
    123123
    124124                'posts' => array(
    125125                        'home',
    126                         'about',
    127                         'contact',
    128                         'blog',
    129                         'homepage-section',
     126                        'about' => array(
     127                                'thumbnail' => '{{featured-image-2}}',
     128                        ),
     129                        'contact' => array(
     130                                'thumbnail' => '{{featured-image-1}}',
     131                        ),
     132                        'blog' => array(
     133                                'thumbnail' => '{{featured-image-3}}',
     134                        ),
     135                        'homepage-section' => array(
     136                                'thumbnail' => '{{featured-image-1}}',
     137                        ),
     138                ),
     139
     140                'attachments' => array(
     141                        'featured-image-1' => array(
     142                                'post_title' => _x( 'Espresso', 'Theme starter content' ),
     143                                'file' => 'assets/images/espresso.jpg',
     144                        ),
     145                        'featured-image-2' => array(
     146                                'post_title' => _x( 'Sandwich', 'Theme starter content' ),
     147                                'file' => 'assets/images/sandwich.jpg',
     148                        ),
     149                        'featured-image-3' => array(
     150                                'post_title' => _x( 'Coffee', 'Theme starter content' ),
     151                                'file' => 'assets/images/coffee.jpg',
     152                        ),
    130153                ),
    131154
    132155                'options' => array(
  • src/wp-includes/class-wp-customize-manager.php

    diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php
    index 439e2b5..8bea976 100644
    final class WP_Customize_Manager { 
    916916                }
    917917
    918918                $sidebars_widgets = isset( $starter_content['widgets'] ) && ! empty( $this->widgets ) ? $starter_content['widgets'] : array();
     919                $attachments = isset( $starter_content['attachments'] ) && ! empty( $this->nav_menus ) ? $starter_content['attachments'] : array();
    919920                $posts = isset( $starter_content['posts'] ) && ! empty( $this->nav_menus ) ? $starter_content['posts'] : array();
    920921                $options = isset( $starter_content['options'] ) ? $starter_content['options'] : array();
    921922                $nav_menus = isset( $starter_content['nav_menus'] ) && ! empty( $this->nav_menus ) ? $starter_content['nav_menus'] : array();
    final class WP_Customize_Manager { 
    965966                        }
    966967                }
    967968
    968                 // Posts & pages.
    969                 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;
     969                $starter_content_auto_draft_post_ids = array();
     970                if ( ! empty( $changeset_data['nav_menus_created_posts']['value'] ) ) {
     971                        $starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, $changeset_data['nav_menus_created_posts']['value'] );
     972                }
     973
     974                $existing_starter_content_posts = array();
     975                if ( ! empty( $starter_content_auto_draft_post_ids ) ) {
     976                        $existing_posts_query = new WP_Query( array(
     977                                'post__in' => $starter_content_auto_draft_post_ids,
     978                                'post_status' => 'auto-draft',
     979                                'post_type' => 'any',
     980                                'number' => -1,
     981                        ) );
     982                        foreach ( $existing_posts_query->posts as $existing_post ) {
     983                                $existing_starter_content_posts[ $existing_post->post_type . ':' . $existing_post->post_name ] = $existing_post;
     984                        }
     985                }
     986
     987                // Attachments are technically posts but handled differently.
     988                if ( ! empty( $attachments ) ) {
     989                        // Such is The WordPress Way.
     990                        require_once( ABSPATH . 'wp-admin/includes/file.php' );
     991                        require_once( ABSPATH . 'wp-admin/includes/media.php' );
     992                        require_once( ABSPATH . 'wp-admin/includes/image.php' );
     993
     994                        $attachment_ids = array();
     995
     996                        foreach ( $attachments as $symbol => $attachment ) {
     997
     998                                // A file is required and URLs to files are not currently allowed.
     999                                if ( empty( $attachment['file'] ) || preg_match( '#^https?://$#', $attachment['file'] ) ) {
     1000                                        continue;
    9851001                                }
     1002
     1003                                $file_array = array();
     1004                                $file_path = null;
     1005                                if ( file_exists( $attachment['file'] ) ) {
     1006                                        $file_path = $attachment['file']; // Could be absolute path to file in plugin.
     1007                                } elseif ( is_child_theme() && file_exists( get_stylesheet_directory() . '/' . $attachment['file'] ) ) {
     1008                                        $file_path = get_stylesheet_directory() . '/' . $attachment['file'];
     1009                                } elseif ( file_exists( get_template_directory() . '/' . $attachment['file'] ) ) {
     1010                                        $file_path = get_template_directory() . '/' . $attachment['file'];
     1011                                } else {
     1012                                        continue;
     1013                                }
     1014                                $file_array['name'] = basename( $attachment['file'] );
     1015
     1016                                // Skip file types that are not recognized.
     1017                                $checked_filetype = wp_check_filetype( $file_array['name'] );
     1018                                if ( empty( $checked_filetype['type'] ) ) {
     1019                                        continue;
     1020                                }
     1021
     1022                                // Ensure post_name is set since not automatically derived from post_title for new auto-draft posts.
     1023                                if ( empty( $attachment['post_name'] ) ) {
     1024                                        if ( ! empty( $attachment['post_title'] ) ) {
     1025                                                $attachment['post_name'] = sanitize_title( $attachment['post_title'] );
     1026                                        } else {
     1027                                                $attachment['post_name'] = sanitize_title( preg_replace( '/\.\w+$/', '', $file_array['name'] ) );
     1028                                        }
     1029                                }
     1030
     1031                                $attachment_id = null;
     1032                                $attached_file = null;
     1033                                if ( isset( $existing_starter_content_posts[ 'attachment:' . $attachment['post_name'] ] ) ) {
     1034                                        $attachment_post = $existing_starter_content_posts[ 'attachment:' . $attachment['post_name'] ];
     1035                                        $attachment_id = $attachment_post->ID;
     1036                                        $attached_file = get_attached_file( $attachment_id );
     1037                                        if ( empty( $attached_file ) || ! file_exists( $attached_file ) ) {
     1038                                                $attachment_id = null;
     1039                                                $attached_file = null;
     1040                                        } elseif ( $this->get_stylesheet() !== get_post_meta( $attachment_post->ID, '_starter_content_theme', true ) ) {
     1041
     1042                                                // Re-generate attachment metadata since it was previously generated for a different theme.
     1043                                                $metadata = wp_generate_attachment_metadata( $attachment_post->ID, $attached_file );
     1044                                                wp_update_attachment_metadata( $attachment_id, $metadata );
     1045                                                update_post_meta( $attachment_id, '_starter_content_theme', $this->get_stylesheet() );
     1046                                        }
     1047                                }
     1048
     1049                                // Insert the attachment auto-draft because it doesn't yet exist or the attached file is gone.
     1050                                if ( ! $attachment_id ) {
     1051
     1052                                        // Copy file to temp location so that original file won't get deleted from theme after sideloading.
     1053                                        $temp_file_name = wp_tempnam( basename( $file_path ) );
     1054                                        if ( $temp_file_name && copy( $file_path, $temp_file_name ) ) {
     1055                                                $file_array['tmp_name'] = $temp_file_name;
     1056                                        }
     1057                                        if ( empty( $file_array['tmp_name'] ) ) {
     1058                                                continue;
     1059                                        }
     1060
     1061                                        $attachment_post_data = array_merge(
     1062                                                wp_array_slice_assoc( $attachment, array( 'post_title', 'post_content', 'post_excerpt', 'post_name' ) ),
     1063                                                array(
     1064                                                        'post_status' => 'auto-draft', // So attachment will be garbage collected in a week if changeset is never published.
     1065                                                )
     1066                                        );
     1067
     1068                                        $attachment_id = media_handle_sideload( $file_array, 0, null, $attachment_post_data );
     1069                                        if ( is_wp_error( $attachment_id ) ) {
     1070                                                continue;
     1071                                        }
     1072                                        update_post_meta( $attachment_id, '_starter_content_theme', $this->get_stylesheet() );
     1073                                }
     1074
     1075                                $attachment_ids[ $symbol ] = $attachment_id;
     1076                                $starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, array_values( $attachment_ids ) );
    9861077                        }
     1078                }
    9871079
     1080                // Posts & pages.
     1081                if ( ! empty( $posts ) ) {
    9881082                        foreach ( array_keys( $posts ) as $post_symbol ) {
    9891083                                if ( empty( $posts[ $post_symbol ]['post_type'] ) ) {
    9901084                                        continue;
    final class WP_Customize_Manager { 
    9991093                                }
    10001094
    10011095                                // 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;
     1096                                if ( isset( $existing_starter_content_posts[ $post_type . ':' . $post_name ] ) ) {
     1097                                        $posts[ $post_symbol ]['ID'] = $existing_starter_content_posts[ $post_type . ':' . $post_name ]->ID;
    10041098                                        continue;
    10051099                                }
    10061100
     1101                                // Translate the featured image symbol.
     1102                                if ( ! empty( $posts[ $post_symbol ]['thumbnail'] )
     1103                                        && preg_match( '/^{{(?P<symbol>.+)}}$/', $posts[ $post_symbol ]['thumbnail'], $matches )
     1104                                        && isset( $attachment_ids[ $matches['symbol'] ] ) ) {
     1105                                        $posts[ $post_symbol ]['meta_input']['_thumbnail_id'] = $attachment_ids[ $matches['symbol'] ];
     1106                                }
     1107
     1108                                if ( ! empty( $posts[ $post_symbol ]['template'] ) ) {
     1109                                        $posts[ $post_symbol ]['meta_input']['_wp_page_template'] = $posts[ $post_symbol ]['template'];
     1110                                }
     1111
    10071112                                $r = $this->nav_menus->insert_auto_draft_post( $posts[ $post_symbol ] );
    10081113                                if ( $r instanceof WP_Post ) {
    10091114                                        $posts[ $post_symbol ]['ID'] = $r->ID;
    10101115                                }
    10111116                        }
    10121117
    1013                         // The nav_menus_created_posts setting is why nav_menus component is dependency for adding posts.
     1118                        $starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, wp_list_pluck( $posts, 'ID' ) );
     1119                }
     1120
     1121                // The nav_menus_created_posts setting is why nav_menus component is dependency for adding posts.
     1122                if ( ! empty( $this->nav_menus ) && ! empty( $starter_content_auto_draft_post_ids ) ) {
    10141123                        $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->pending_starter_content_settings_ids[] = $setting_id;
    1019                         }
     1124                        $this->set_post_value( $setting_id, array_unique( array_values( $starter_content_auto_draft_post_ids ) ) );
     1125                        $this->pending_starter_content_settings_ids[] = $setting_id;
    10201126                }
    10211127
    10221128                // Nav menus.
  • src/wp-includes/class-wp-customize-nav-menus.php

    diff --git src/wp-includes/class-wp-customize-nav-menus.php src/wp-includes/class-wp-customize-nav-menus.php
    index 7ce9e18..1305a14 100644
    final class WP_Customize_Nav_Menus { 
    11911191                $post_ids = $setting->post_value();
    11921192                if ( ! empty( $post_ids ) ) {
    11931193                        foreach ( $post_ids as $post_id ) {
     1194                                $target_status = 'attachment' === get_post_type( $post_id ) ? 'inherit' : 'publish';
     1195
    11941196                                // Note that wp_publish_post() cannot be used because unique slugs need to be assigned.
    1195                                 wp_update_post( array( 'ID' => $post_id, 'post_status' => 'publish' ) );
     1197                                wp_update_post( array( 'ID' => $post_id, 'post_status' => $target_status ) );
    11961198                        }
    11971199                }
    11981200        }
  • src/wp-includes/post.php

    diff --git src/wp-includes/post.php src/wp-includes/post.php
    index d3c68ca..620d1e4 100644
    function wp_insert_post( $postarr, $wp_error = false ) { 
    30583058        }
    30593059
    30603060        $post_status = empty( $postarr['post_status'] ) ? 'draft' : $postarr['post_status'];
    3061         if ( 'attachment' === $post_type && ! in_array( $post_status, array( 'inherit', 'private', 'trash' ) ) ) {
     3061        if ( 'attachment' === $post_type && ! in_array( $post_status, array( 'inherit', 'private', 'trash', 'auto-draft' ), true ) ) {
    30623062                $post_status = 'inherit';
    30633063        }
    30643064
  • src/wp-includes/theme.php

    diff --git src/wp-includes/theme.php src/wp-includes/theme.php
    index 13bf9ca..d3b688b 100644
    function get_theme_starter_content() { 
    19811981                        // Widgets are grouped into sidebars.
    19821982                        case 'widgets' :
    19831983                                foreach ( $config[ $type ] as $sidebar_id => $widgets ) {
    1984                                         foreach ( $widgets as $widget ) {
     1984                                        foreach ( $widgets as $id => $widget ) {
    19851985                                                if ( is_array( $widget ) ) {
     1986
     1987                                                        // Item extends core content.
     1988                                                        if ( ! empty( $core_content[ $type ][ $id ] ) ) {
     1989                                                                $widget = array(
     1990                                                                        $core_content[ $type ][ $id ][0],
     1991                                                                        array_merge( $core_content[ $type ][ $id ][1], $widget ),
     1992                                                                );
     1993                                                        }
     1994
    19861995                                                        $content[ $type ][ $sidebar_id ][] = $widget;
    19871996                                                } elseif ( is_string( $widget ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $widget ] ) ) {
    19881997                                                        $content[ $type ][ $sidebar_id ][] = $core_content[ $type ][ $widget ];
    function get_theme_starter_content() { 
    20022011
    20032012                                        $content[ $type ][ $nav_menu_location ]['name'] = $nav_menu['name'];
    20042013
    2005                                         foreach ( $nav_menu['items'] as $nav_menu_item ) {
     2014                                        foreach ( $nav_menu['items'] as $id => $nav_menu_item ) {
    20062015                                                if ( is_array( $nav_menu_item ) ) {
     2016
     2017                                                        // Item extends core content.
     2018                                                        if ( ! empty( $core_content[ $type ][ $id ] ) ) {
     2019                                                                $nav_menu_item = array_merge( $core_content[ $type ][ $id ], $nav_menu_item );
     2020                                                        }
     2021
    20072022                                                        $content[ $type ][ $nav_menu_location ]['items'][] = $nav_menu_item;
    20082023                                                } elseif ( is_string( $nav_menu_item ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $nav_menu_item ] ) ) {
    20092024                                                        $content[ $type ][ $nav_menu_location ]['items'][] = $core_content[ $type ][ $nav_menu_item ];
    function get_theme_starter_content() { 
    20122027                                }
    20132028                                break;
    20142029
    2015                         // Everything else should map at the next level.
    2016                         default :
    2017                                 foreach( $config[ $type ] as $i => $item ) {
     2030                        // Attachments are posts but have special treatment.
     2031                        case 'attachments' :
     2032                                foreach ( $config[ $type ] as $id => $item ) {
     2033                                        if ( ! empty( $item['file'] ) ) {
     2034                                                $content[ $type ][ $id ] = $item;
     2035                                        }
     2036                                }
     2037                                break;
     2038
     2039                        // All that's left now are posts (besides attachments). Not a default case for the sake of clarity and future work.
     2040                        case 'posts' :
     2041                                foreach ( $config[ $type ] as $id => $item ) {
    20182042                                        if ( is_array( $item ) ) {
    2019                                                 $content[ $type ][ $i ] = $item;
    2020                                         } elseif ( is_string( $item ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $item ] ) ) {
     2043
     2044                                                // Item extends core content.
     2045                                                if ( ! empty( $core_content[ $type ][ $id ] ) ) {
     2046                                                        $item = array_merge( $core_content[ $type ][ $id ], $item );
     2047                                                }
     2048
     2049                                                // Enforce a subset of fields.
     2050                                                $content[ $type ][ $id ] = wp_array_slice_assoc(
     2051                                                        $item,
     2052                                                        array(
     2053                                                                'post_type',
     2054                                                                'post_title',
     2055                                                                'post_excerpt',
     2056                                                                'post_name',
     2057                                                                'post_content',
     2058                                                                'menu_order',
     2059                                                                'comment_status',
     2060                                                                'thumbnail',
     2061                                                                'template',
     2062                                                        )
     2063                                                );
     2064                                        } elseif ( is_string( $item ) && ! empty( $core_content[ $type ][ $item ] ) ) {
    20212065                                                $content[ $type ][ $item ] = $core_content[ $type ][ $item ];
    20222066                                        }
    20232067                                }
  • tests/phpunit/tests/customize/manager.php

    diff --git tests/phpunit/tests/customize/manager.php tests/phpunit/tests/customize/manager.php
    index 8596cee..ea6fd10 100644
    class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    313313         */
    314314        function test_import_theme_starter_content() {
    315315                wp_set_current_user( self::$admin_user_id );
     316                register_nav_menu( 'top', 'Top' );
    316317
    317318                global $wp_customize;
    318319                $wp_customize = new WP_Customize_Manager();
    class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    343344                        ),
    344345                        'posts' => array(
    345346                                'home',
    346                                 'about',
     347                                'about' => array(
     348                                        'template' => 'sample-page-template.php',
     349                                ),
    347350                                'blog',
    348351                                'custom' => array(
    349352                                        'post_type' => 'post',
    350353                                        'post_title' => 'Custom',
     354                                        'thumbnail' => '{{featured-image-logo}}',
     355                                ),
     356                        ),
     357                        'attachments' => array(
     358                                'featured-image-logo' => array(
     359                                        'post_title' => 'Featured Image',
     360                                        'post_content' => 'Attachment Description',
     361                                        'post_excerpt' => 'Attachment Caption',
     362                                        'file' => ABSPATH . '/wp-includes/images/w-logo-blue.png',
    351363                                ),
    352364                        ),
    353365                        'options' => array(
    class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    394406                $this->assertEquals( array( 'text-2', 'meta-3' ), $changeset_values['sidebars_widgets[sidebar-1]'] );
    395407
    396408                $posts_by_name = array();
     409                $this->assertCount( 5, $changeset_values['nav_menus_created_posts'] );
    397410                foreach ( $changeset_values['nav_menus_created_posts'] as $post_id ) {
    398411                        $post = get_post( $post_id );
    399412                        $this->assertEquals( 'auto-draft', $post->post_status );
    400413                        $posts_by_name[ $post->post_name ] = $post->ID;
    401414                }
     415                $this->assertEquals( array( 'featured-image', 'home', 'about', 'blog', 'custom' ), array_keys( $posts_by_name ) );
     416                $this->assertEquals( 'Custom', get_post( $posts_by_name['custom'] )->post_title );
     417                $this->assertEquals( 'sample-page-template.php', get_page_template_slug( $posts_by_name['about'] ) );
     418                $this->assertEquals( '', get_page_template_slug( $posts_by_name['blog'] ) );
     419                $this->assertEquals( $posts_by_name['featured-image'], get_post_thumbnail_id( $posts_by_name['custom'] ) );
     420                $this->assertEquals( '', get_post_thumbnail_id( $posts_by_name['blog'] ) );
     421                $attachment_metadata = wp_get_attachment_metadata( $posts_by_name['featured-image'] );
     422                $this->assertEquals( 'Featured Image', get_post( $posts_by_name['featured-image'] )->post_title );
     423                $this->assertArrayHasKey( 'file', $attachment_metadata );
     424                $this->assertContains( 'w-logo-blue', $attachment_metadata['file'] );
    402425
    403426                $this->assertEquals( 'page', $changeset_values['show_on_front'] );
    404427                $this->assertEquals( $posts_by_name['home'], $changeset_values['page_on_front'] );
    class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    418441                        $this->assertTrue( $setting_params['starter_content'] );
    419442                }
    420443
     444                // Ensure that re-importing doesn't cause auto-drafts to balloon.
     445                $wp_customize->import_theme_starter_content();
     446                $changeset_data = $wp_customize->changeset_data();
     447                $this->assertEqualSets( array_values( $posts_by_name ), $changeset_data['nav_menus_created_posts']['value'], 'Auto-drafts should not get re-created and amended with each import.' );
     448
    421449                // Test that saving non-starter content on top of the changeset clears the starter_content flag.
    422450                $wp_customize->save_changeset_post( array(
    423451                        'data' => array(
    class Tests_WP_Customize_Manager extends WP_UnitTestCase { 
    442470                $this->assertArrayNotHasKey( 'starter_content', $changeset_data['blogname'] );
    443471                $this->assertNotEquals( $previous_blogdescription, $changeset_data['blogdescription']['value'] );
    444472                $this->assertArrayHasKey( 'starter_content', $changeset_data['blogdescription'] );
     473
     474                // Publish.
     475                $this->assertEquals( 'auto-draft', get_post( $posts_by_name['about'] )->post_status );
     476                $this->assertEquals( 'auto-draft', get_post( $posts_by_name['featured-image'] )->post_status );
     477                $this->assertNotEquals( $changeset_data['blogname']['value'], get_option( 'blogname' ) );
     478                $r = $wp_customize->save_changeset_post( array( 'status' => 'publish' ) );
     479                $this->assertInternalType( 'array', $r );
     480                $this->assertEquals( 'publish', get_post( $posts_by_name['about'] )->post_status );
     481                $this->assertEquals( 'inherit', get_post( $posts_by_name['featured-image'] )->post_status );
     482                $this->assertEquals( $changeset_data['blogname']['value'], get_option( 'blogname' ) );
    445483        }
    446484
    447485        /**
  • tests/phpunit/tests/theme/getThemeStarterContent.php

    diff --git tests/phpunit/tests/theme/getThemeStarterContent.php tests/phpunit/tests/theme/getThemeStarterContent.php
    index 59505f1..1c28b06 100644
    class Tests_WP_Theme_Get_Theme_Starter_Content extends WP_UnitTestCase { 
    4141                        'widgets' => array(
    4242                                'sidebar-1' => array(
    4343                                        'text_business_info',
    44                                         'text_about',
     44                                        'text_about' => array(
     45                                                'title' => 'Our Story',
     46                                        ),
    4547                                        'archives',
    4648                                        'calendar',
    4749                                        'categories',
    class Tests_WP_Theme_Get_Theme_Starter_Content extends WP_UnitTestCase { 
    6365                                                'page_about',
    6466                                                'page_blog',
    6567                                                'page_news',
    66                                                 'page_contact',
     68                                                'page_contact' => array(
     69                                                        'title' => 'Email Us',
     70                                                ),
    6771                                                'link_email',
    6872                                                'link_facebook',
    6973                                                'link_foursquare',
    class Tests_WP_Theme_Get_Theme_Starter_Content extends WP_UnitTestCase { 
    8690                                'home',
    8791                                'about',
    8892                                'contact',
    89                                 'blog',
     93                                'blog' => array(
     94                                        'template' => 'blog.php',
     95                                        'post_excerpt' => 'Extended',
     96                                ),
    9097                                'news',
    9198                                'homepage-section',
    9299                                'unknown',
    93100                                'custom' => array(
    94101                                        'post_type' => 'post',
    95102                                        'post_title' => 'Custom',
     103                                        'thumbnail' => '{{featured-image-logo}}',
     104                                ),
     105                        ),
     106                        'attachments' => array(
     107                                'featured-image-logo' => array(
     108                                        'post_title' => 'Title',
     109                                        'post_content' => 'Description',
     110                                        'post_excerpt' => 'Caption',
     111                                        'file' => ABSPATH . '/wp-includes/images/w-logo-blue.png',
     112                                ),
     113                                'featured-image-skipped' => array(
     114                                        'post_title' => 'Skipped',
    96115                                ),
    97116                        ),
    98117                        'options' => array(
    class Tests_WP_Theme_Get_Theme_Starter_Content extends WP_UnitTestCase { 
    115134                $this->assertSame( $hydrated_starter_content['options'], $dehydrated_starter_content['options'] );
    116135                $this->assertCount( 16, $hydrated_starter_content['nav_menus']['top']['items'], 'Unknown should be dropped, custom should be present.' );
    117136                $this->assertCount( 10, $hydrated_starter_content['widgets']['sidebar-1'], 'Unknown should be dropped.' );
     137                $this->assertCount( 1, $hydrated_starter_content['attachments'], 'Attachment with missing file is filtered out.' );
     138                $this->assertArrayHasKey( 'featured-image-logo', $hydrated_starter_content['attachments'] );
     139                $this->assertSame( $dehydrated_starter_content['attachments']['featured-image-logo'], $hydrated_starter_content['attachments']['featured-image-logo'] );
    118140
    119141                foreach ( $hydrated_starter_content['widgets']['sidebar-1'] as $widget ) {
    120142                        $this->assertInternalType( 'array', $widget );
    class Tests_WP_Theme_Get_Theme_Starter_Content extends WP_UnitTestCase { 
    123145                        $this->assertInternalType( 'array', $widget[1] );
    124146                        $this->assertArrayHasKey( 'title', $widget[1] );
    125147                }
     148                $this->assertEquals( 'text', $hydrated_starter_content['widgets']['sidebar-1'][1][0], 'Core content extended' );
     149                $this->assertEquals( 'Our Story', $hydrated_starter_content['widgets']['sidebar-1'][1][1]['title'], 'Core content extended' );
    126150
    127151                foreach ( $hydrated_starter_content['nav_menus']['top']['items'] as $nav_menu_item ) {
    128152                        $this->assertInternalType( 'array', $nav_menu_item );
    129153                        $this->assertTrue( ! empty( $nav_menu_item['object_id'] ) || ! empty( $nav_menu_item['url'] ) );
    130154                }
     155                $this->assertEquals( 'Email Us', $hydrated_starter_content['nav_menus']['top']['items'][4]['title'], 'Core content extended' );
    131156
    132157                foreach ( $hydrated_starter_content['posts'] as $key => $post ) {
    133158                        $this->assertInternalType( 'string', $key );
    class Tests_WP_Theme_Get_Theme_Starter_Content extends WP_UnitTestCase { 
    136161                        $this->assertArrayHasKey( 'post_type', $post );
    137162                        $this->assertArrayHasKey( 'post_title', $post );
    138163                }
     164                $this->assertEquals( 'Extended', $hydrated_starter_content['posts']['blog']['post_excerpt'], 'Core content extended' );
     165                $this->assertEquals( 'blog.php', $hydrated_starter_content['posts']['blog']['template'], 'Core content extended' );
     166                $this->assertEquals( '{{featured-image-logo}}', $hydrated_starter_content['posts']['custom']['thumbnail'], 'Core content extended' );
    139167        }
    140168
    141169        /**