Make WordPress Core

Changeset 41651


Ignore:
Timestamp:
09/30/2017 01:14:34 AM (9 years 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.