WordPress.org

Make WordPress Core

Changeset 41651


Ignore:
Timestamp:
09/30/17 01:14:34 (8 weeks ago)
Author:
westonruter
Message:

Embeds: Cache oEmbeds in an oembed_cache custom post type instead of postmeta when there is no global $post.

Add processing of embeds to rich Text widget.

Props swissspidy, westonruter, ocean90, johnbillion.
See #40854, #39994, #40935.
Fixes #34115.

Location:
trunk
Files:
4 edited

Legend:

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

    r41162 r41651  
    3131        // Hack to get the [embed] shortcode to run before wpautop() 
    3232        add_filter( 'the_content', array( $this, 'run_shortcode' ), 8 ); 
     33        add_filter( 'widget_text_content', array( $this, 'run_shortcode' ), 8 ); 
    3334 
    3435        // Shortcode placeholder for strip_shortcodes() 
     
    3738        // Attempts to embed all URLs in a post 
    3839        add_filter( 'the_content', array( $this, 'autoembed' ), 8 ); 
     40        add_filter( 'widget_text_content', array( $this, 'autoembed' ), 8 ); 
    3941 
    4042        // After a post is saved, cache oEmbed items via Ajax 
     
    186188 
    187189        $post_ID = ( ! empty( $post->ID ) ) ? $post->ID : null; 
    188         if ( ! empty( $this->post_ID ) ) // Potentially set by WP_Embed::cache_oembed() 
     190 
     191        // Potentially set by WP_Embed::cache_oembed(). 
     192        if ( ! empty( $this->post_ID ) ) { 
    189193            $post_ID = $this->post_ID; 
    190  
    191         // Unknown URL format. Let oEmbed have a go. 
     194        } 
     195 
     196        // Check for a cached result (stored as custom post or in the post meta). 
     197        $key_suffix    = md5( $url . serialize( $attr ) ); 
     198        $cachekey      = '_oembed_' . $key_suffix; 
     199        $cachekey_time = '_oembed_time_' . $key_suffix; 
     200 
     201        /** 
     202         * Filters the oEmbed TTL value (time to live). 
     203         * 
     204         * @since 4.0.0 
     205         * 
     206         * @param int    $time    Time to live (in seconds). 
     207         * @param string $url     The attempted embed URL. 
     208         * @param array  $attr    An array of shortcode attributes. 
     209         * @param int    $post_ID Post ID. 
     210         */ 
     211        $ttl = apply_filters( 'oembed_ttl', DAY_IN_SECONDS, $url, $attr, $post_ID ); 
     212 
     213        $cache      = ''; 
     214        $cache_time = 0; 
     215 
     216        $cached_post_id = $this->find_oembed_post_id( $key_suffix ); 
     217 
    192218        if ( $post_ID ) { 
    193  
    194             // Check for a cached result (stored in the post meta) 
    195             $key_suffix = md5( $url . serialize( $attr ) ); 
    196             $cachekey = '_oembed_' . $key_suffix; 
    197             $cachekey_time = '_oembed_time_' . $key_suffix; 
    198  
    199             /** 
    200              * Filters the oEmbed TTL value (time to live). 
    201              * 
    202              * @since 4.0.0 
    203              * 
    204              * @param int    $time    Time to live (in seconds). 
    205              * @param string $url     The attempted embed URL. 
    206              * @param array  $attr    An array of shortcode attributes. 
    207              * @param int    $post_ID Post ID. 
    208              */ 
    209             $ttl = apply_filters( 'oembed_ttl', DAY_IN_SECONDS, $url, $attr, $post_ID ); 
    210  
    211219            $cache = get_post_meta( $post_ID, $cachekey, true ); 
    212220            $cache_time = get_post_meta( $post_ID, $cachekey_time, true ); 
     
    215223                $cache_time = 0; 
    216224            } 
    217  
    218             $cached_recently = ( time() - $cache_time ) < $ttl; 
    219  
    220             if ( $this->usecache || $cached_recently ) { 
    221                 // Failures are cached. Serve one if we're using the cache. 
    222                 if ( '{{unknown}}' === $cache ) 
    223                     return $this->maybe_make_link( $url ); 
    224  
    225                 if ( ! empty( $cache ) ) { 
    226                     /** 
    227                      * Filters the cached oEmbed HTML. 
    228                      * 
    229                      * @since 2.9.0 
    230                      * 
    231                      * @see WP_Embed::shortcode() 
    232                      * 
    233                      * @param mixed  $cache   The cached HTML result, stored in post meta. 
    234                      * @param string $url     The attempted embed URL. 
    235                      * @param array  $attr    An array of shortcode attributes. 
    236                      * @param int    $post_ID Post ID. 
    237                      */ 
    238                     return apply_filters( 'embed_oembed_html', $cache, $url, $attr, $post_ID ); 
    239                 } 
    240             } 
    241  
    242             /** 
    243              * Filters whether to inspect the given URL for discoverable link tags. 
    244              * 
    245              * @since 2.9.0 
    246              * @since 4.4.0 The default value changed to true. 
    247              * 
    248              * @see WP_oEmbed::discover() 
    249              * 
    250              * @param bool $enable Whether to enable `<link>` tag discovery. Default true. 
    251              */ 
    252             $attr['discover'] = ( apply_filters( 'embed_oembed_discover', true ) ); 
    253  
    254             // Use oEmbed to get the HTML 
    255             $html = wp_oembed_get( $url, $attr ); 
    256  
    257             // Maybe cache the result 
     225        } elseif ( $cached_post_id ) { 
     226            $cached_post = get_post( $cached_post_id ); 
     227 
     228            $cache      = $cached_post->post_content; 
     229            $cache_time = strtotime( $cached_post->post_modified_gmt ); 
     230        } 
     231 
     232        $cached_recently = ( time() - $cache_time ) < $ttl; 
     233 
     234        if ( $this->usecache || $cached_recently ) { 
     235            // Failures are cached. Serve one if we're using the cache. 
     236            if ( '{{unknown}}' === $cache ) { 
     237                return $this->maybe_make_link( $url ); 
     238            } 
     239 
     240            if ( ! empty( $cache ) ) { 
     241                /** 
     242                 * Filters the cached oEmbed HTML. 
     243                 * 
     244                 * @since 2.9.0 
     245                 * 
     246                 * @see WP_Embed::shortcode() 
     247                 * 
     248                 * @param mixed  $cache   The cached HTML result, stored in post meta. 
     249                 * @param string $url     The attempted embed URL. 
     250                 * @param array  $attr    An array of shortcode attributes. 
     251                 * @param int    $post_ID Post ID. 
     252                 */ 
     253                return apply_filters( 'embed_oembed_html', $cache, $url, $attr, $post_ID ); 
     254            } 
     255        } 
     256 
     257        /** 
     258         * Filters whether to inspect the given URL for discoverable link tags. 
     259         * 
     260         * @since 2.9.0 
     261         * @since 4.4.0 The default value changed to true. 
     262         * 
     263         * @see WP_oEmbed::discover() 
     264         * 
     265         * @param bool $enable Whether to enable `<link>` tag discovery. Default true. 
     266         */ 
     267        $attr['discover'] = apply_filters( 'embed_oembed_discover', true ); 
     268 
     269        // Use oEmbed to get the HTML. 
     270        $html = wp_oembed_get( $url, $attr ); 
     271 
     272        if ( $post_ID ) { 
    258273            if ( $html ) { 
    259274                update_post_meta( $post_ID, $cachekey, $html ); 
     
    262277                update_post_meta( $post_ID, $cachekey, '{{unknown}}' ); 
    263278            } 
    264  
    265             // If there was a result, return it 
    266             if ( $html ) { 
    267                 /** This filter is documented in wp-includes/class-wp-embed.php */ 
    268                 return apply_filters( 'embed_oembed_html', $html, $url, $attr, $post_ID ); 
    269             } 
     279        } else { 
     280            $has_kses = false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ); 
     281 
     282            if ( $has_kses ) { 
     283                // Prevent KSES from corrupting JSON in post_content. 
     284                kses_remove_filters(); 
     285            } 
     286 
     287            wp_insert_post( wp_slash( array( 
     288                'post_name'    => $key_suffix, 
     289                'post_content' => $html ? $html : '{{unknown}}', 
     290                'post_status'  => 'publish', 
     291                'post_type'    => 'oembed_cache', 
     292            ) ) ); 
     293 
     294            if ( $has_kses ) { 
     295                kses_init_filters(); 
     296            } 
     297        } 
     298 
     299        // If there was a result, return it. 
     300        if ( $html ) { 
     301            /** This filter is documented in wp-includes/class-wp-embed.php */ 
     302            return apply_filters( 'embed_oembed_html', $html, $url, $attr, $post_ID ); 
    270303        } 
    271304 
     
    383416        return apply_filters( 'embed_maybe_make_link', $output, $url ); 
    384417    } 
     418 
     419    /** 
     420     * Find the oEmbed cache post ID for a given cache key. 
     421     * 
     422     * @since 4.9.0 
     423     * 
     424     * @param string $cache_key oEmbed cache key. 
     425     * @return int|null Post ID on success, null on failure. 
     426     */ 
     427    public function find_oembed_post_id( $cache_key ) { 
     428        $cache_group    = 'oembed_cache_post'; 
     429        $oembed_post_id = wp_cache_get( $cache_key, $cache_group ); 
     430 
     431        if ( $oembed_post_id && 'oembed_cache' === get_post_type( $oembed_post_id ) ) { 
     432            return $oembed_post_id; 
     433        } 
     434 
     435        $oembed_post_query = new WP_Query( array( 
     436            'post_type'              => 'oembed_cache', 
     437            'post_status'            => 'publish', 
     438            'name'                   => $cache_key, 
     439            'posts_per_page'         => 1, 
     440            'no_found_rows'          => true, 
     441            'cache_results'          => true, 
     442            'update_post_meta_cache' => false, 
     443            'update_post_term_cache' => false, 
     444            'lazy_load_term_meta'    => false, 
     445        ) ); 
     446 
     447        if ( ! empty( $oembed_post_query->posts ) ) { 
     448            // Note: 'fields'=>'ids' is not being used in order to cache the post object as it will be needed. 
     449            $oembed_post_id = $oembed_post_query->posts[0]->ID; 
     450            wp_cache_set( $cache_key, $oembed_post_id, $cache_group ); 
     451 
     452            return $oembed_post_id; 
     453        } 
     454 
     455        return null; 
     456    } 
    385457} 
  • trunk/src/wp-includes/post.php

    r41642 r41651  
    194194            'read_private_posts' => 'customize', 
    195195        ), 
     196    ) ); 
     197 
     198    register_post_type( 'oembed_cache', array( 
     199        'labels' => array( 
     200            'name'          => __( 'oEmbed Responses' ), 
     201            'singular_name' => __( 'oEmbed Response' ), 
     202        ), 
     203        'public'           => false, 
     204        'hierarchical'     => false, 
     205        'rewrite'          => false, 
     206        'query_var'        => false, 
     207        'delete_with_user' => false, 
     208        'can_export'       => false, 
     209        '_builtin'         => true, /* internal use only. don't use this when registering your own post type. */ 
     210        'supports'         => array(), 
    196211    ) ); 
    197212 
     
    30043019 *     @type array  $post_category         Array of category names, slugs, or IDs. 
    30053020 *                                         Defaults to value of the 'default_category' option. 
    3006  *     @type array  $tags_input            Array of tag names, slugs, or IDs. Default empty.  
     3021 *     @type array  $tags_input            Array of tag names, slugs, or IDs. Default empty. 
    30073022 *     @type array  $tax_input             Array of taxonomy terms keyed by their taxonomy name. Default empty. 
    30083023 *     @type array  $meta_input            Array of post meta values keyed by their post meta key. Default empty. 
  • trunk/tests/phpunit/tests/external-http/oembed.php

    r35178 r41651  
    3636        global $wp_embed; 
    3737        $out = $wp_embed->autoembed( 'https://www.youtube.com/embed/QcIy9NiNbmo' ); 
    38         $this->assertContains( 'https://youtube.com/watch?v=QcIy9NiNbmo', $out ); 
     38        $this->assertContains( 'https://www.youtube.com/embed/QcIy9NiNbmo?feature=oembed', $out ); 
    3939    } 
    4040 
     
    4242        global $wp_embed; 
    4343        $out = $wp_embed->autoembed( 'https://www.youtube.com/v/QcIy9NiNbmo' ); 
    44         $this->assertContains( 'https://youtube.com/watch?v=QcIy9NiNbmo', $out ); 
     44        $this->assertContains( 'https://www.youtube.com/embed/QcIy9NiNbmo?feature=oembed', $out ); 
    4545    } 
    4646} 
  • trunk/tests/phpunit/tests/oembed/WpEmbed.php

    r38762 r41651  
    162162    } 
    163163 
    164     public function test_shortcode_should_cache_data_in_post_meta_for_known_post() { 
    165         $GLOBALS['post'] = $this->factory()->post->create_and_get(); 
     164    public function test_shortcode_should_get_cached_data_from_post_meta_for_known_post() { 
     165        global $post; 
     166 
     167        $post = $this->factory()->post->create_and_get(); 
    166168        $url             = 'https://example.com/'; 
    167169        $expected        = '<b>Embedded content</b>'; 
    168170        $key_suffix      = md5( $url . serialize( wp_embed_defaults( $url ) ) ); 
    169171        $cachekey        = '_oembed_' . $key_suffix; 
    170         $cachekey_time   = '_oembed_time_' . $key_suffix; 
     172 
     173        add_post_meta( $post->ID, $cachekey, $expected ); 
    171174 
    172175        add_filter( 'pre_oembed_result', array( $this, '_pre_oembed_result_callback' ) ); 
     
    174177        remove_filter( 'pre_oembed_result', array( $this, '_pre_oembed_result_callback' ) ); 
    175178 
     179        $actual_2 = $this->wp_embed->shortcode( array(), $url ); 
     180 
     181        $cached = get_post_meta( $post->ID, $cachekey, true ); 
     182 
     183        // Cleanup. 
     184        unset( $post ); 
     185 
    176186        $this->assertEquals( $expected, $actual ); 
    177  
    178         $this->assertEquals( $expected, get_post_meta( $GLOBALS['post']->ID, $cachekey, true ) ); 
    179         $this->assertNotEmpty( get_post_meta( $GLOBALS['post']->ID, $cachekey_time, true ) ); 
    180  
    181         // Result should be cached. 
    182         $actual = $this->wp_embed->shortcode( array(), $url ); 
    183         $this->assertEquals( $expected, $actual ); 
    184     } 
    185  
    186     public function test_shortcode_should_cache_failure_in_post_meta_for_known_post() { 
    187         $GLOBALS['post'] = $this->factory()->post->create_and_get(); 
     187        $this->assertEquals( $expected, $actual_2 ); 
     188        $this->assertEquals( $expected, $cached ); 
     189    } 
     190 
     191    public function test_shortcode_should_get_cached_failure_from_post_meta_for_known_post() { 
     192        global $post; 
     193 
     194        $post = $this->factory()->post->create_and_get(); 
    188195        $url             = 'https://example.com/'; 
    189196        $expected        = '<a href="' . esc_url( $url ) . '">' . esc_html( $url ) . '</a>'; 
     
    192199        $cachekey_time   = '_oembed_time_' . $key_suffix; 
    193200 
     201        add_post_meta( $post->ID, $cachekey, '{{unknown}}' ); 
     202        add_post_meta( $post->ID, $cachekey_time, 0 ); 
     203 
    194204        add_filter( 'pre_oembed_result', '__return_empty_string' ); 
    195205        $actual = $this->wp_embed->shortcode( array(), $url ); 
    196206        remove_filter( 'pre_oembed_result', '__return_empty_string' ); 
    197207 
     208        // Result should be cached. 
     209        $actual_2 = $this->wp_embed->shortcode( array(), $url ); 
     210 
     211        $cached = get_post_meta( $post->ID, $cachekey, true ); 
     212        $cached_time = get_post_meta( $post->ID, $cachekey_time, true ); 
     213 
     214        // Cleanup. 
     215        unset( $post ); 
     216 
    198217        $this->assertEquals( $expected, $actual ); 
    199  
    200         $this->assertEquals( '{{unknown}}', get_post_meta( $GLOBALS['post']->ID, $cachekey, true ) ); 
    201         $this->assertEmpty( get_post_meta( $GLOBALS['post']->ID, $cachekey_time, true ) ); 
     218        $this->assertEquals( '{{unknown}}', $cached ); 
     219        $this->assertEmpty( $cached_time ); 
     220        $this->assertEquals( $expected, $actual_2 ); 
     221    } 
     222 
     223    /** 
     224     * @ticket 34115 
     225     */ 
     226    public function test_shortcode_should_cache_data_in_custom_post() { 
     227        $url        = 'https://example.com/'; 
     228        $expected   = '<b>Embedded content</b>'; 
     229        $key_suffix = md5( $url . serialize( wp_embed_defaults( $url ) ) ); 
     230 
     231        add_filter( 'pre_oembed_result', array( $this, '_pre_oembed_result_callback' ) ); 
     232        $actual = $this->wp_embed->shortcode( array(), $url ); 
     233        remove_filter( 'pre_oembed_result', array( $this, '_pre_oembed_result_callback' ) ); 
     234 
     235        $oembed_post_id = $this->wp_embed->find_oembed_post_id( $key_suffix ); 
     236        $post_content = get_post( $oembed_post_id )->post_content; 
    202237 
    203238        // Result should be cached. 
    204         $actual = $this->wp_embed->shortcode( array(), $url ); 
     239        $actual_2 = $this->wp_embed->shortcode( array(), $url ); 
     240 
     241        wp_delete_post( $oembed_post_id ); 
     242 
     243        $this->assertNotNull( $oembed_post_id ); 
     244        $this->assertEquals( $expected, $post_content ); 
    205245        $this->assertEquals( $expected, $actual ); 
     246        $this->assertEquals( $expected, $actual_2 ); 
     247    } 
     248 
     249    /** 
     250     * @ticket 34115 
     251     */ 
     252    public function test_shortcode_should_cache_failure_in_custom_post() { 
     253        $url        = 'https://example.com/'; 
     254        $expected   = '<a href="' . esc_url( $url ) . '">' . esc_html( $url ) . '</a>'; 
     255        $key_suffix = md5( $url . serialize( wp_embed_defaults( $url ) ) ); 
     256 
     257        add_filter( 'pre_oembed_result', '__return_empty_string' ); 
     258        $actual = $this->wp_embed->shortcode( array(), $url ); 
     259        remove_filter( 'pre_oembed_result', '__return_empty_string' ); 
     260 
     261        $oembed_post_id = $this->wp_embed->find_oembed_post_id( $key_suffix ); 
     262        $post_content = get_post( $oembed_post_id )->post_content; 
     263 
     264        // Result should be cached. 
     265        $actual_2 = $this->wp_embed->shortcode( array(), $url ); 
     266 
     267        wp_delete_post( $oembed_post_id ); 
     268 
     269        $this->assertEquals( $expected, $actual ); 
     270        $this->assertEquals( $expected, $actual_2 ); 
     271        $this->assertNotNull( $oembed_post_id ); 
     272        $this->assertEquals( '{{unknown}}', $post_content ); 
    206273    } 
    207274 
Note: See TracChangeset for help on using the changeset viewer.