WordPress.org

Make WordPress Core

Ticket #10337: 10337.workinprogress.3.patch

File 10337.workinprogress.3.patch, 14.0 KB (added by Viper007Bond, 9 years ago)

Move oEmbed caching to an AJAX request after the post is saved

  • wp-admin/admin-ajax.php

     
    111111
    112112        die('0');
    113113        break;
     114case 'oembed-cache' :
     115        $return = ( $wp_embed->cache_oembed( $_GET['post'] ) ) ? '1' : '0';
     116        die($return);
     117        break;
    114118default :
    115119        do_action( 'wp_ajax_' . $_GET['action'] );
    116120        die('0');
  • wp-includes/class-oembed.php

     
     1<?php
     2
     3class WP_oEmbed {
     4        var $providers = array();
     5
     6        function WP_oEmbed() {
     7                return $this->__construct();
     8        }
     9
     10        function __construct() {
     11                // List out some popular sites, mainly ones that don't have discovery tags in their <head>
     12                $this->providers = apply_filters( 'oembed_providers', array(
     13                        'http://*.flickr.com/*'       => 'http://www.flickr.com/services/oembed/',
     14                        'http://*.viddler.com/*'      => 'http://lab.viddler.com/services/oembed/',
     15                        'http://qik.com/*'            => 'http://qik.com/api/oembed.{format}',
     16                        'http://*.revision3.com/*'    => 'http://revision3.com/api/oembed/',
     17                        'http://www.hulu.com/watch/*' => 'http://www.hulu.com/api/oembed.{format}',
     18                        //'http://www.vimeo.com/*'      => 'http://www.vimeo.com/api/oembed.{format}', // Discovery tag supported, leave it commented to use as a discovery test
     19
     20                        //'http://*.youtube.com/watch*' => 'http://oohembed.com/oohembed/',
     21                ) );
     22        }
     23
     24        // Pass a URL, returns false or HTML result
     25        function getHTML( $url, $args = '' ) {
     26                $provider = false;
     27
     28                foreach ( $this->providers as $scheme => $providerurl ) { // Needs a better variable name
     29
     30                        // Turn the asterisk-type provider URLs into regex
     31                        $regex = '#' . str_replace( '___wildcard___', '(.+)', preg_quote( str_replace( '*', '___wildcard___', $scheme ) ) ) . '#i';
     32
     33                        if ( preg_match( $regex, $url ) ) {
     34                                $provider = str_replace( '{format}', 'json', $providerurl ); // JSON is easier to deal with than XML
     35                                break;
     36                        }
     37                }
     38
     39                if ( !$provider )
     40                        $provider = $this->discover( $url );
     41
     42                if ( !$provider || false === $data = $this->fetch( $provider, $url, $args ) )
     43                        return false;
     44
     45                return $this->data2html( $data, $url );
     46        }
     47
     48        // Pass a URL, returns the oEmbed provider URL(s) if it can find them in the <head>
     49        function discover( $url ) {
     50                $providers = array();
     51
     52                // Fetch URL content
     53                if ( $html = wp_remote_retrieve_body( wp_remote_get( $url ) ) ) {
     54
     55                        // <link> types that contain oEmbed provider URLs
     56                        $linktypes = apply_filters( 'oembed_linktypes', array(
     57                                'application/json+oembed' => 'json',
     58                                'text/xml+oembed' => 'xml',
     59                                'application/xml+oembed' => 'xml', // Incorrect, but used by at least Vimeo
     60                        ) );
     61
     62                        // Strip <body>
     63                        $html = substr( $html, 0, stripos( $html, '</head>' ) );
     64
     65                        // Do a quick check
     66                        $tagfound = false;
     67                        foreach ( $linktypes as $linktype => $format ) {
     68                                if ( stripos($html, $linktype) ) {
     69                                        $tagfound = true;
     70                                        break;
     71                                }
     72                        }
     73
     74                        if ( $tagfound && preg_match_all( '/<link([^<>]+)>/i', $html, $links ) ) {
     75                                foreach ( $links[1] as $link ) {
     76                                        $atts = shortcode_parse_atts( $link );
     77
     78                                        if ( !empty($atts['type']) && !empty($linktypes[$atts['type']]) && !empty($atts['href']) ) {
     79                                                $providers[$linktypes[$atts['type']]] = $atts['href'];
     80
     81                                                // Stop here if it's JSON (that's all we need)
     82                                                if ( 'json' == $linktypes[$atts['type']] )
     83                                                        break;
     84                                        }
     85                                }
     86                        }
     87                }
     88
     89                // JSON is preferred to XML
     90                if ( !empty($providers['json']) )
     91                        return $providers['json'];
     92                elseif ( !empty($providers['xml']) )
     93                        return $providers['xml'];
     94                else
     95                        return false;
     96        }
     97
     98        // Connects to an oEmbed provider and returns the result
     99        function fetch( $provider, $url, $args = '' ) {
     100                $args = wp_parse_args( $args, wp_embed_defaults() );
     101
     102                $provider = add_query_arg( 'format', 'json', $provider ); // JSON is easier to deal with than XML
     103
     104                $provider = add_query_arg( 'maxwidth', $args['width'], $provider );
     105                $provider = add_query_arg( 'maxheight', $args['height'], $provider );
     106                $provider = add_query_arg( 'url', urlencode($url), $provider );
     107
     108                if ( !$result = wp_remote_retrieve_body( wp_remote_get( $provider ) ) )
     109                        return false;
     110
     111                $data = false;
     112
     113                // JSON?
     114                // Example: http://vimeo.com/api/oembed.json?url=http%3A%2F%2Fvimeo.com%2F240975
     115                if ( $data = json_decode($result) ) {
     116                        return $data;
     117                }
     118
     119                // Must be XML. Start out with the super-easy PHP5 functions.
     120                // Example: http://vimeo.com/api/oembed.xml?url=http%3A%2F%2Fvimeo.com%2F240975
     121                elseif ( function_exists('simplexml_load_string') ) {
     122                        libxml_use_internal_errors('true');
     123
     124                        $data = simplexml_load_string($result);
     125
     126                        if ( is_object($data) )
     127                                return $data;
     128                }
     129
     130                // Still must be XML, but no PHP5 support. Use PHP4 (ugh).
     131                // Example: http://vimeo.com/api/oembed.xml?url=http%3A%2F%2Fvimeo.com%2F240975
     132                else {
     133                        // Code me!
     134                }
     135
     136                return false;
     137        }
     138
     139        // Takes a data set and returns the HTML
     140        function data2html( $data, $url ) {
     141                if ( !is_object($data) || empty($data->type) )
     142                        return false;
     143
     144                switch ( $data->type ) {
     145                        case 'photo':
     146                                if ( empty($data->url) || empty($data->width) || empty($data->height) )
     147                                        return false;
     148
     149                                $title = ( !empty($data->title) ) ? $data->title : '';
     150                                return '<img src="' . esc_attr( clean_url( $data->url ) ) . '" alt="' . esc_attr($title) . '" width="' . esc_attr($data->width) . '" height="' . esc_attr($data->height) . '" />';
     151
     152                        case 'video':
     153                        case 'rich':
     154                                return ( !empty($data->html) ) ? $data->html : false;
     155
     156                        case 'link':
     157                                return ( !empty($data->title) ) ? '<a href="' . clean_url($url) . '">' . esc_html($data->title) . '</a>' : false;
     158                }
     159
     160                return false;
     161        }
     162}
     163
     164function &_wp_oembed_get_object() {
     165        static $oembed;
     166
     167        if ( is_null($oembed) )
     168                $oembed = new WP_oEmbed();
     169
     170        return $oembed;
     171}
  • wp-includes/media.php

     
    933933
    934934        return false;
    935935}
     936
     937
     938
     939
     940
     941
     942
     943
     944
     945// Embed class
     946class WP_Embed {
     947        var $handlers = array();
     948        var $post_ID;
     949        var $usecache = true;
     950        var $linkifunknown = true;
     951
     952        // PHP4 constructor
     953        function WP_Embed() {
     954                return $this->__construct();
     955        }
     956
     957        // PHP5 constructor
     958        function __construct() {
     959                // After a post is saved, cache oEmbed items via AJAX
     960                add_action( 'edit_form_advanced', array(&$this, 'maybe_run_ajax_cache') );
     961
     962                // Hack to get the [embed] shortcode to run before wpautop()
     963                add_filter( 'the_content', array(&$this, 'runshortcode'), 9 );
     964
     965                // Per Matt's idea, this attempts to embed all URLs in a post
     966                add_filter( 'the_content', array(&$this, 'autoembed'), 9 );
     967        }
     968
     969
     970        // Run do_shortcode() with only the [embed] shortcode active
     971        // This is so it can be run earlier than normal
     972        function runshortcode( $content ) {
     973                global $shortcode_tags;
     974
     975                // Backup current registered shortcodes and clear them all out
     976                $orig_shortcode_tags = $shortcode_tags;
     977                remove_all_shortcodes();
     978
     979                add_shortcode( 'embed', array(&$this, 'shortcode') );
     980
     981                // Do the shortcode (only the [embed] one is registered)
     982                $content = do_shortcode( $content );
     983
     984                // Put the original shortcodes back
     985                $shortcode_tags = $orig_shortcode_tags;
     986
     987                return $content;
     988        }
     989
     990        // If a post was saved, then cache oEmbed results via AJAX
     991        function maybe_run_ajax_cache() {
     992                global $post_ID;
     993
     994                if ( empty($_GET['message']) || 1 != $_GET['message'] )
     995                        return;
     996
     997?>
     998<script type="text/javascript">
     999/* <![CDATA[ */
     1000        jQuery(document).ready(function($){
     1001                $.get("<?php echo admin_url( 'admin-ajax.php?action=oembed-cache&post=' . $post_ID ); ?>");
     1002        });
     1003/* ]]> */
     1004</script>
     1005<?php
     1006        }
     1007
     1008
     1009        // Register a handler for WP_Embed::shortcode()
     1010        // wp_embed_register_handler() is a wrapper for this
     1011        function register_handler( $id, $regex, $callback, $priority = 10 ) {
     1012                $this->handlers[$priority][$id] = array(
     1013                        'regex'    => $regex,
     1014                        'callback' => $callback,
     1015                );
     1016        }
     1017
     1018        // [embed] handler
     1019        function shortcode( $atts, $url = '' ) {
     1020                global $post;
     1021
     1022                $atts = wp_parse_args( $atts, wp_embed_defaults() );
     1023
     1024                // Allow plugins to add their own handler
     1025                if ( false !== $filter = apply_filters( 'embed_other_before', false, $atts, $url ) )
     1026                        return $filter;
     1027
     1028                // Look for known internal handlers
     1029                ksort( $this->handlers );
     1030                foreach ( $this->handlers as $handlers ) {
     1031                        foreach ( $handlers as $id => $handler ) {
     1032                                if ( preg_match( $handler['regex'], $url, $matches ) && is_callable( $handler['callback'] ) ) {
     1033                                        return call_user_func( $handler['callback'], $matches, $atts, $url );
     1034                                }
     1035                        }
     1036                }
     1037
     1038                // Unknown URL format. Let oEmbed have a go.
     1039                if ( current_user_can('unfiltered_html') ) {
     1040                        $cachekey = '_oembed_' . md5( $url . implode( '|', $atts ) );
     1041
     1042                        $post_ID = ( !empty($post->ID) ) ? $post->ID : null;
     1043                        if ( !empty($this->post_ID) ) // Potentially set by WP_Embed::cache_oembed()
     1044                                $post_ID = $this->post_ID;
     1045
     1046                        // Check for a cached result (stored in the post meta)
     1047                        if ( $post_ID && $this->usecache ) {
     1048                                $cache = get_post_meta( $post_ID, $cachekey, true );
     1049
     1050                                // Failures are cached
     1051                                if ( '{{unknown}}' === $cache )
     1052                                        return $this->make_link( $url );
     1053
     1054                                if ( !empty($cache) )
     1055                                        return $cache;
     1056                        }
     1057
     1058                        $html = wp_oembed_get( $url, $atts );
     1059
     1060                        // Cache the result
     1061                        if ( $post_ID ) {
     1062                                $cache = ( $html ) ? $html : '{{unknown}}';
     1063                                update_post_meta( $post_ID, $cachekey, $cache );
     1064                        }
     1065
     1066                        if ( $html )
     1067                                return $html;
     1068                }
     1069
     1070                // Allow plugins to add their own handler
     1071                if ( false !== $filter = apply_filters( 'embed_other_after', false, $atts, $url ) )
     1072                        return $filter;
     1073
     1074                // Still unknown
     1075                return $this->make_link( $url );
     1076        }
     1077
     1078        // On save_post, run the shortcode on the post in order to trigger WP_Embed::shortcode() to cache oEmbed results
     1079        function cache_oembed( $post_ID ) {
     1080                $post = get_post( $post_ID );
     1081
     1082                if ( empty($post->ID) )
     1083                        return false;
     1084
     1085                if ( !in_array( $post->post_type, array( 'post', 'page' ) ) )
     1086                        return;
     1087
     1088                // Dump existing caches
     1089                $post_metas = get_post_custom_keys( $post->ID );
     1090                foreach( $post_metas as $post_meta_key ) {
     1091                        if ( '_oembed_' == substr( $post_meta_key, 0, 8 ) )
     1092                                delete_post_meta($post->ID, $post_meta_key );
     1093                }
     1094
     1095                // Trigger a caching
     1096                if ( !empty($post->post_content) ) {
     1097                        $this->post_ID = $post->ID;
     1098                        $this->usecache = false;
     1099                        $this->autoembed( $this->runshortcode( $post->post_content ) );
     1100                        $this->usecache = true;
     1101                }
     1102
     1103                return true;
     1104        }
     1105
     1106        // Helper function that makes a HTML link to the passed URL
     1107        function make_link( $url ) {
     1108                return ( $this->linkifunknown ) ? apply_filters( 'embed_urllink', '<a href="' . esc_attr($url) . '">' . esc_html($url) . '</a>' ) : $url;
     1109        }
     1110
     1111        // With help from WP_Emebed::_autoembed(), this attempts to convert all URLs in a post into embeds
     1112        // This incorrectly turns links where the clickable text is a URL into an embed
     1113        // For example, it incorectly processes this: <a href="http://example.com/">http://example.com/</a>
     1114        function autoembed( $content ) {
     1115                // Stolen from make_clickable()
     1116                $content = ' ' . $content;
     1117                $content = preg_replace_callback('#(?<=[\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#$%&~/\-=?@\[\](+]|[.,;:](?![\s<])|(?(1)\)(?![\s<])|\)))+)#is',  array(&$this, '_autoembed'), $content);
     1118                return trim( $content );
     1119        }
     1120
     1121        // A helper function for WP_Embed::autoembed() that converts a URL into an embed
     1122        function _autoembed( $match ) {
     1123                $this->linkifunknown = false;
     1124                $return = $this->shortcode( array(), $match[2] );
     1125                $this->linkifunknown = true;
     1126                return $return;
     1127        }
     1128}
     1129
     1130$wp_embed = new WP_Embed();
     1131
     1132// Wrapper to register a new hander for a URL format inside [embed]
     1133function wp_embed_register_handler( $id, $regex, $callback ) {
     1134        global $wp_embed;
     1135        return $wp_embed->register_handler( $id, $regex, $callback );
     1136}
     1137
     1138// A helper function to return some default attributes for embeds
     1139function wp_embed_defaults() {
     1140        return apply_filters( 'embed_defaults', array(
     1141                'width' => 500,
     1142                'height' => 700,
     1143        ) );
     1144}
     1145
     1146// Based on a supplied width/height ratio, return the biggest possible dimensions based on the max width/height
     1147function wp_embed_calcdims( $example_width, $example_height, $max_width, $max_height ) {
     1148        return wp_constrain_dimensions( $example_width * 1000000, $example_height * 1000000, $max_width, $max_height );
     1149}
     1150
     1151// Wrapper to load the WP_oEmbed class and use it to get some HTML
     1152function wp_oembed_get( $url, $args = '' ) {
     1153        require_once( 'class-oembed.php' );
     1154        $oembed_object = _wp_oembed_get_object();
     1155        return $oembed_object->getHTML( $url, $args );
     1156}
     1157
     1158
     1159
     1160// YouTube doesn't support oEmbed, so handle it internally
     1161wp_embed_register_handler( 'youtube', '#http://(www.youtube|youtube|[A-Za-z]{2}.youtube)\.com/(watch\?v=|w/\?v=|\?v=)([\w-]+)(.*?)#i', 'wp_embed_handler_youtube' );
     1162function wp_embed_handler_youtube( $matches, $atts, $url ) {
     1163        list($width, $height) = wp_embed_calcdims( 425, 344, $atts['width'], $atts['height'] );
     1164        return "<object width='$width' height='$height'><param name='movie' value='http://www.youtube.com/v/{$matches[3]}&amp;hl=en&amp;fs=1'></param><param name='allowFullScreen' value='true'></param><param name='allowscriptaccess' value='always'></param><embed src='http://www.youtube.com/v/{$matches[3]}&amp;hl=en&amp;fs=1' type='application/x-shockwave-flash' allowscriptaccess='always' allowfullscreen='true' width='$width' height='$height'></embed></object>";
     1165}
     1166
     1167
     1168// PollDaddy
     1169wp_embed_register_handler( 'polldaddy', '#http://answers.polldaddy.com/poll/(\d+)(.*?)#i', 'wp_embed_handler_polldaddy' );
     1170function wp_embed_handler_polldaddy( $matches, $atts, $url ) {
     1171        return '<script type="text/javascript" src="http://s3.polldaddy.com/p/' . esc_attr($matches[1]) . '"></script>';
     1172}
     1173 No newline at end of file