Make WordPress Core

Changeset 39346


Ignore:
Timestamp:
11/23/2016 09:52:27 AM (8 years ago)
Author:
helen
Message:

Theme starter content: Add support for featured images and page templates.

Featured image support means that attachments can now be imported. Media can be sideloaded from within theme or plugin directories. Like other posts, attachments are auto-drafts until customizer changes are published, and are not duplicated when they already exist in the customized state. Attachment IDs can be used for any number of purposes, much like post IDs. Twenty Seventeen now includes 3 images used as featured images to best showcase the multi-section homepage setup.

As featured image IDs are stored in post meta, it also made sense to add support for page templates. Twenty Seventeen does not include any such templates, but the functionality can be quite important for displaying themes to their best effect.

props westonruter, helen, flixos90.
fixes #38615.

Location:
trunk
Files:
3 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-content/themes/twentyseventeen/functions.php

    r39317 r39346  
    124124        'posts' => array(
    125125            'home',
    126             'about',
    127             'contact',
    128             'blog',
    129             'homepage-section',
     126            'about' => array(
     127                'thumbnail' => '{{image-sandwich}}',
     128            ),
     129            'contact' => array(
     130                'thumbnail' => '{{image-espresso}}',
     131            ),
     132            'blog' => array(
     133                'thumbnail' => '{{image-coffee}}',
     134            ),
     135            'homepage-section' => array(
     136                'thumbnail' => '{{image-espresso}}',
     137            ),
     138        ),
     139
     140        'attachments' => array(
     141            'image-espresso' => array(
     142                'post_title' => _x( 'Espresso', 'Theme starter content' ),
     143                'file' => 'assets/images/espresso.jpg',
     144            ),
     145            'image-sandwich' => array(
     146                'post_title' => _x( 'Sandwich', 'Theme starter content' ),
     147                'file' => 'assets/images/sandwich.jpg',
     148            ),
     149            'image-coffee' => array(
     150                'post_title' => _x( 'Coffee', 'Theme starter content' ),
     151                'file' => 'assets/images/coffee.jpg',
     152            ),
    130153        ),
    131154
  • trunk/src/wp-includes/class-wp-customize-manager.php

    r39345 r39346  
    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();
     
    966967        }
    967968
     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;
     1001                }
     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                    // In PHP < 5.6 filesize() returns 0 for the temp files unless we clear the file status cache.
     1069                    // Technically, PHP < 5.6.0 || < 5.5.13 || < 5.4.29 but no need to be so targeted.
     1070                    // See https://bugs.php.net/bug.php?id=65701
     1071                    if ( version_compare( PHP_VERSION, '5.6', '<' ) ) {
     1072                        clearstatcache();
     1073                    }
     1074
     1075                    $attachment_id = media_handle_sideload( $file_array, 0, null, $attachment_post_data );
     1076                    if ( is_wp_error( $attachment_id ) ) {
     1077                        continue;
     1078                    }
     1079                    update_post_meta( $attachment_id, '_starter_content_theme', $this->get_stylesheet() );
     1080                }
     1081
     1082                $attachment_ids[ $symbol ] = $attachment_id;
     1083                $starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, array_values( $attachment_ids ) );
     1084            }
     1085        }
     1086
    9681087        // Posts & pages.
    9691088        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 
    9881089            foreach ( array_keys( $posts ) as $post_symbol ) {
    9891090                if ( empty( $posts[ $post_symbol ]['post_type'] ) ) {
     
    10001101
    10011102                // 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;
     1103                if ( isset( $existing_starter_content_posts[ $post_type . ':' . $post_name ] ) ) {
     1104                    $posts[ $post_symbol ]['ID'] = $existing_starter_content_posts[ $post_type . ':' . $post_name ]->ID;
    10041105                    continue;
     1106                }
     1107
     1108                // Translate the featured image symbol.
     1109                if ( ! empty( $posts[ $post_symbol ]['thumbnail'] )
     1110                    && preg_match( '/^{{(?P<symbol>.+)}}$/', $posts[ $post_symbol ]['thumbnail'], $matches )
     1111                    && isset( $attachment_ids[ $matches['symbol'] ] ) ) {
     1112                    $posts[ $post_symbol ]['meta_input']['_thumbnail_id'] = $attachment_ids[ $matches['symbol'] ];
     1113                }
     1114
     1115                if ( ! empty( $posts[ $post_symbol ]['template'] ) ) {
     1116                    $posts[ $post_symbol ]['meta_input']['_wp_page_template'] = $posts[ $post_symbol ]['template'];
    10051117                }
    10061118
     
    10111123            }
    10121124
    1013             // The nav_menus_created_posts setting is why nav_menus component is dependency for adding posts.
     1125            $starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, wp_list_pluck( $posts, 'ID' ) );
     1126        }
     1127
     1128        // The nav_menus_created_posts setting is why nav_menus component is dependency for adding posts.
     1129        if ( ! empty( $this->nav_menus ) && ! empty( $starter_content_auto_draft_post_ids ) ) {
    10141130            $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             }
     1131            $this->set_post_value( $setting_id, array_unique( array_values( $starter_content_auto_draft_post_ids ) ) );
     1132            $this->pending_starter_content_settings_ids[] = $setting_id;
    10201133        }
    10211134
  • trunk/src/wp-includes/class-wp-customize-nav-menus.php

    r39241 r39346  
    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        }
  • trunk/src/wp-includes/post.php

    r39278 r39346  
    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    }
  • trunk/src/wp-includes/theme.php

    r39338 r39346  
    19871987            case 'widgets' :
    19881988                foreach ( $config[ $type ] as $sidebar_id => $widgets ) {
    1989                     foreach ( $widgets as $widget ) {
     1989                    foreach ( $widgets as $id => $widget ) {
    19901990                        if ( is_array( $widget ) ) {
     1991
     1992                            // Item extends core content.
     1993                            if ( ! empty( $core_content[ $type ][ $id ] ) ) {
     1994                                $widget = array(
     1995                                    $core_content[ $type ][ $id ][0],
     1996                                    array_merge( $core_content[ $type ][ $id ][1], $widget ),
     1997                                );
     1998                            }
     1999
    19912000                            $content[ $type ][ $sidebar_id ][] = $widget;
    19922001                        } elseif ( is_string( $widget ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $widget ] ) ) {
     
    20082017                    $content[ $type ][ $nav_menu_location ]['name'] = $nav_menu['name'];
    20092018
    2010                     foreach ( $nav_menu['items'] as $nav_menu_item ) {
     2019                    foreach ( $nav_menu['items'] as $id => $nav_menu_item ) {
    20112020                        if ( is_array( $nav_menu_item ) ) {
     2021
     2022                            // Item extends core content.
     2023                            if ( ! empty( $core_content[ $type ][ $id ] ) ) {
     2024                                $nav_menu_item = array_merge( $core_content[ $type ][ $id ], $nav_menu_item );
     2025                            }
     2026
    20122027                            $content[ $type ][ $nav_menu_location ]['items'][] = $nav_menu_item;
    20132028                        } elseif ( is_string( $nav_menu_item ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $nav_menu_item ] ) ) {
     
    20182033                break;
    20192034
    2020             // Everything else should map at the next level.
    2021             default :
    2022                 foreach( $config[ $type ] as $i => $item ) {
     2035            // Attachments are posts but have special treatment.
     2036            case 'attachments' :
     2037                foreach ( $config[ $type ] as $id => $item ) {
     2038                    if ( ! empty( $item['file'] ) ) {
     2039                        $content[ $type ][ $id ] = $item;
     2040                    }
     2041                }
     2042                break;
     2043
     2044            // All that's left now are posts (besides attachments). Not a default case for the sake of clarity and future work.
     2045            case 'posts' :
     2046                foreach ( $config[ $type ] as $id => $item ) {
    20232047                    if ( is_array( $item ) ) {
    2024                         $content[ $type ][ $i ] = $item;
    2025                     } elseif ( is_string( $item ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $item ] ) ) {
     2048
     2049                        // Item extends core content.
     2050                        if ( ! empty( $core_content[ $type ][ $id ] ) ) {
     2051                            $item = array_merge( $core_content[ $type ][ $id ], $item );
     2052                        }
     2053
     2054                        // Enforce a subset of fields.
     2055                        $content[ $type ][ $id ] = wp_array_slice_assoc(
     2056                            $item,
     2057                            array(
     2058                                'post_type',
     2059                                'post_title',
     2060                                'post_excerpt',
     2061                                'post_name',
     2062                                'post_content',
     2063                                'menu_order',
     2064                                'comment_status',
     2065                                'thumbnail',
     2066                                'template',
     2067                            )
     2068                        );
     2069                    } elseif ( is_string( $item ) && ! empty( $core_content[ $type ][ $item ] ) ) {
    20262070                        $content[ $type ][ $item ] = $core_content[ $type ][ $item ];
    20272071                    }
  • trunk/tests/phpunit/tests/customize/manager.php

    r39332 r39346  
    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;
     
    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' => DIR_TESTDATA . '/images/waffles.jpg',
    351363                ),
    352364            ),
     
    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 );
     
    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( 'waffles', $attachment_metadata['file'] );
    402425
    403426        $this->assertEquals( 'page', $changeset_values['show_on_front'] );
     
    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(
     
    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
  • trunk/tests/phpunit/tests/theme/getThemeStarterContent.php

    r39276 r39346  
    4242                'sidebar-1' => array(
    4343                    'text_business_info',
    44                     'text_about',
     44                    'text_about' => array(
     45                        'title' => 'Our Story',
     46                    ),
    4547                    'archives',
    4648                    'calendar',
     
    6466                        'page_blog',
    6567                        'page_news',
    66                         'page_contact',
     68                        'page_contact' => array(
     69                            'title' => 'Email Us',
     70                        ),
    6771                        'link_email',
    6872                        'link_facebook',
     
    8791                'about',
    8892                'contact',
    89                 'blog',
     93                'blog' => array(
     94                    'template' => 'blog.php',
     95                    'post_excerpt' => 'Extended',
     96                ),
    9097                'news',
    9198                'homepage-section',
     
    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' => DIR_TESTDATA . '/images/waffles.jpg',
     112                ),
     113                'featured-image-skipped' => array(
     114                    'post_title' => 'Skipped',
    96115                ),
    97116            ),
     
    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 ) {
     
    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 ) {
     
    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 ) {
     
    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
Note: See TracChangeset for help on using the changeset viewer.