| | 936 | |
| | 937 | |
| | 938 | |
| | 939 | |
| | 940 | |
| | 941 | |
| | 942 | /* |
| | 943 | |
| | 944 | EMBED TODO: |
| | 945 | |
| | 946 | * oEmbed PHP4 XML |
| | 947 | * Videos in feeds? It's currently just a link (handlers manually; WP_Embed class aborts before oEmbed). |
| | 948 | Note that <object> and <embed> breaks feed validation last I looked, but perhaps we don't care? |
| | 949 | * Phpdoc |
| | 950 | * Auto-embed: Possible to modify regex to ignore inside of <a> tags? The hack that's currently used isn't that robust. |
| | 951 | * Add more default handlers (the WP.com ones perhaps?) |
| | 952 | * Should the stuff be wrapped in a div/span for styling purposes? Maybe leave it to a filter? |
| | 953 | * Add a bunch more filters (oEmbed output, handler outputs, etc.). Add common filter to all output too. Suggestions welcome! |
| | 954 | |
| | 955 | */ |
| | 956 | |
| | 957 | |
| | 958 | // Embed class |
| | 959 | class WP_Embed { |
| | 960 | var $handlers = array(); |
| | 961 | var $post_ID; |
| | 962 | var $usecache = true; |
| | 963 | var $linkifunknown = true; |
| | 964 | var $autoembedcontent; |
| | 965 | |
| | 966 | // PHP4 constructor |
| | 967 | function WP_Embed() { |
| | 968 | return $this->__construct(); |
| | 969 | } |
| | 970 | |
| | 971 | // PHP5 constructor |
| | 972 | function __construct() { |
| | 973 | // After a post is saved, cache oEmbed items via AJAX |
| | 974 | add_action( 'edit_form_advanced', array(&$this, 'maybe_run_ajax_cache') ); |
| | 975 | |
| | 976 | // Hack to get the [embed] shortcode to run before wpautop() |
| | 977 | add_filter( 'the_content', array(&$this, 'runshortcode'), 9 ); |
| | 978 | |
| | 979 | // Attempts to embed all URLs in a post |
| | 980 | if ( get_option('embed_autourls') ) |
| | 981 | add_filter( 'the_content', array(&$this, 'autoembed'), 12 ); |
| | 982 | } |
| | 983 | |
| | 984 | // Run do_shortcode() with only the [embed] shortcode active |
| | 985 | // This is so it can be run earlier than normal so the output can go through wpautop() |
| | 986 | function runshortcode( $content ) { |
| | 987 | global $shortcode_tags; |
| | 988 | |
| | 989 | // Backup current registered shortcodes and clear them all out |
| | 990 | $orig_shortcode_tags = $shortcode_tags; |
| | 991 | remove_all_shortcodes(); |
| | 992 | |
| | 993 | add_shortcode( 'embed', array(&$this, 'shortcode') ); |
| | 994 | |
| | 995 | // Do the shortcode (only the [embed] one is registered) |
| | 996 | $content = do_shortcode( $content ); |
| | 997 | |
| | 998 | // Put the original shortcodes back |
| | 999 | $shortcode_tags = $orig_shortcode_tags; |
| | 1000 | |
| | 1001 | return $content; |
| | 1002 | } |
| | 1003 | |
| | 1004 | // If a post was saved, then cache oEmbed results via AJAX |
| | 1005 | function maybe_run_ajax_cache() { |
| | 1006 | global $post_ID; |
| | 1007 | |
| | 1008 | if ( empty($post_ID) || empty($_GET['message']) || 1 != $_GET['message'] ) |
| | 1009 | return; |
| | 1010 | |
| | 1011 | ?> |
| | 1012 | <script type="text/javascript"> |
| | 1013 | /* <![CDATA[ */ |
| | 1014 | jQuery(document).ready(function($){ |
| | 1015 | $.get("<?php echo admin_url( 'admin-ajax.php?action=oembed-cache&post=' . $post_ID ); ?>"); |
| | 1016 | }); |
| | 1017 | /* ]]> */ |
| | 1018 | </script> |
| | 1019 | <?php |
| | 1020 | } |
| | 1021 | |
| | 1022 | // Register an internal handler for WP_Embed::shortcode() |
| | 1023 | // wp_embed_register_handler() is a wrapper for this, so call that instead |
| | 1024 | // This should only be used for sites that don't support oEmbed |
| | 1025 | function register_handler( $id, $regex, $callback, $priority = 10 ) { |
| | 1026 | $this->handlers[$priority][$id] = array( |
| | 1027 | 'regex' => $regex, |
| | 1028 | 'callback' => $callback, |
| | 1029 | ); |
| | 1030 | } |
| | 1031 | |
| | 1032 | // Remove a previously registered internal handler |
| | 1033 | // wp_embed_unregister_handler() is a wrapper for this, so call that instead |
| | 1034 | function unregister_handler( $id, $priority = 10 ) { |
| | 1035 | if ( isset($this->handlers[$priority][$id]) ) |
| | 1036 | unset($this->handlers[$priority][$id]); |
| | 1037 | } |
| | 1038 | |
| | 1039 | // [embed] shortcode handler |
| | 1040 | function shortcode( $attr, $url = '' ) { |
| | 1041 | global $post, $_wp_using_ext_object_cache; |
| | 1042 | |
| | 1043 | if ( empty($url) ) |
| | 1044 | return ''; |
| | 1045 | |
| | 1046 | $rawattr = $attr; |
| | 1047 | $attr = wp_parse_args( $attr, wp_embed_defaults() ); |
| | 1048 | |
| | 1049 | // Look for known internal handlers |
| | 1050 | ksort( $this->handlers ); |
| | 1051 | foreach ( $this->handlers as $priority => $handlers ) { |
| | 1052 | foreach ( $handlers as $id => $handler ) { |
| | 1053 | if ( preg_match( $handler['regex'], $url, $matches ) && is_callable( $handler['callback'] ) ) { |
| | 1054 | if ( false !== $return = call_user_func( $handler['callback'], $matches, $attr, $url, $rawattr ) ) |
| | 1055 | return $return; |
| | 1056 | } |
| | 1057 | } |
| | 1058 | } |
| | 1059 | |
| | 1060 | if ( is_feed() ) |
| | 1061 | return $this->make_link( $url ); |
| | 1062 | |
| | 1063 | // Unknown URL format. Let oEmbed have a go. |
| | 1064 | if ( current_user_can('unfiltered_html') ) { |
| | 1065 | $post_ID = ( !empty($post->ID) ) ? $post->ID : null; |
| | 1066 | if ( !empty($this->post_ID) ) // Potentially set by WP_Embed::cache_oembed() |
| | 1067 | $post_ID = $this->post_ID; |
| | 1068 | |
| | 1069 | // Check for a cached result (stored in the post meta) |
| | 1070 | if ( $post_ID ) { |
| | 1071 | $cachekey = '_oembed_' . md5( $url . implode( '|', $attr ) ); |
| | 1072 | |
| | 1073 | if ( $this->usecache ) { |
| | 1074 | $cache = ( $_wp_using_ext_object_cache ) ? wp_cache_get( "{$post_ID}_{$cachekey}", 'oembed' ) : get_post_meta( $post_ID, $cachekey, true ); |
| | 1075 | |
| | 1076 | // Failures are cached |
| | 1077 | if ( '{{unknown}}' === $cache ) |
| | 1078 | return $this->make_link( $url ); |
| | 1079 | |
| | 1080 | if ( !empty($cache) ) |
| | 1081 | return $cache; |
| | 1082 | } |
| | 1083 | } |
| | 1084 | |
| | 1085 | $html = wp_oembed_get( $url, $attr ); |
| | 1086 | |
| | 1087 | // Cache the result |
| | 1088 | if ( $post_ID ) { |
| | 1089 | $cache = ( $html ) ? $html : '{{unknown}}'; |
| | 1090 | |
| | 1091 | if ( $_wp_using_ext_object_cache ) |
| | 1092 | wp_cache_set( "{$post_ID}_{$cachekey}", $cache, 'oembed' ); |
| | 1093 | else |
| | 1094 | update_post_meta( $post_ID, $cachekey, $cache ); |
| | 1095 | } |
| | 1096 | |
| | 1097 | if ( $html ) |
| | 1098 | return $html; |
| | 1099 | } |
| | 1100 | |
| | 1101 | // Still unknown |
| | 1102 | return $this->make_link( $url ); |
| | 1103 | } |
| | 1104 | |
| | 1105 | // Cache all oEmbed results for the given post ID to it's post meta |
| | 1106 | function cache_oembed( $post_ID ) { |
| | 1107 | $post = get_post( $post_ID ); |
| | 1108 | |
| | 1109 | // post_type check is incase of "save_post" usage |
| | 1110 | if ( empty($post->ID) || !in_array( $post->post_type, array( 'post', 'page' ) ) ) |
| | 1111 | return; |
| | 1112 | |
| | 1113 | // Dump existing caches |
| | 1114 | $post_metas = get_post_custom_keys( $post->ID ); |
| | 1115 | foreach( $post_metas as $post_meta_key ) { |
| | 1116 | if ( '_oembed_' == substr( $post_meta_key, 0, 8 ) ) |
| | 1117 | delete_post_meta( $post->ID, $post_meta_key ); |
| | 1118 | } |
| | 1119 | |
| | 1120 | // Trigger a caching |
| | 1121 | if ( !empty($post->post_content) ) { |
| | 1122 | $this->post_ID = $post->ID; |
| | 1123 | $this->usecache = false; |
| | 1124 | |
| | 1125 | $content = $this->runshortcode( $post->post_content ); |
| | 1126 | if ( get_option('embed_autourls') ) |
| | 1127 | $this->autoembed( $content ); |
| | 1128 | |
| | 1129 | $this->usecache = true; |
| | 1130 | } |
| | 1131 | } |
| | 1132 | |
| | 1133 | // Helper function that conditionally makes a HTML link for the passed URL |
| | 1134 | function make_link( $url ) { |
| | 1135 | return ( $this->linkifunknown ) ? make_link($url) : $url; |
| | 1136 | } |
| | 1137 | |
| | 1138 | // With help from WP_Emebed::_autoembed(), this attempts to convert all URLs in a post into embeds |
| | 1139 | function autoembed( $content ) { |
| | 1140 | // Stolen from make_clickable() |
| | 1141 | $content = ' ' . $content; |
| | 1142 | $this->autoembedcontent = $content; |
| | 1143 | $content = preg_replace_callback( '#(?<=[\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#$%&~/\-=?@\[\](+]|[.,;:](?![\s<])|(?(1)\)(?![\s<])|\)))+)#is', array(&$this, '_autoembed'), $content ); |
| | 1144 | $this->autoembedcontent = null; |
| | 1145 | return trim( $content ); |
| | 1146 | } |
| | 1147 | |
| | 1148 | // A helper function for WP_Embed::autoembed() that converts a URL into an embed |
| | 1149 | function _autoembed( $match ) { |
| | 1150 | $return = $match[0]; |
| | 1151 | |
| | 1152 | // If the match was not directly before </a> (i.e. it's not the click text of a link), |
| | 1153 | // then process it using the shortcode handler |
| | 1154 | if ( !strpos( $this->autoembedcontent, $match[0] . '</a>' ) ) { // This isn't perfect, ex: the URL is in the content twice |
| | 1155 | $oldval = $this->linkifunknown; |
| | 1156 | $this->linkifunknown = false; |
| | 1157 | $return = $this->shortcode( array(), $match[2] ); |
| | 1158 | $this->linkifunknown = $oldval; |
| | 1159 | } |
| | 1160 | |
| | 1161 | return $match[1] . $return; |
| | 1162 | } |
| | 1163 | } |
| | 1164 | $wp_embed = new WP_Embed(); |
| | 1165 | |
| | 1166 | |
| | 1167 | // Wrapper to register a new hander for a URL format inside [embed] |
| | 1168 | function wp_embed_register_handler( $id, $regex, $callback, $priority = 10 ) { |
| | 1169 | global $wp_embed; |
| | 1170 | return $wp_embed->register_handler( $id, $regex, $callback, $priority ); |
| | 1171 | } |
| | 1172 | |
| | 1173 | // Wrapper to unregister a new hander for a URL format inside [embed] |
| | 1174 | function wp_embed_unregister_handler( $id, $priority = 10 ) { |
| | 1175 | global $wp_embed; |
| | 1176 | return $wp_embed->unregister_handler( $id, $priority ); |
| | 1177 | } |
| | 1178 | |
| | 1179 | // A helper function to return some default attributes for embeds |
| | 1180 | function wp_embed_defaults() { |
| | 1181 | if ( !empty($GLOBALS['content_width']) ) |
| | 1182 | $theme_width = (int) $GLOBALS['content_width']; |
| | 1183 | |
| | 1184 | if ( !($width = get_option('embed_size_w')) && !empty($theme_width) ) |
| | 1185 | $width = $theme_width; |
| | 1186 | |
| | 1187 | if ( !$width ) |
| | 1188 | $width = 500; |
| | 1189 | |
| | 1190 | return apply_filters( 'embed_defaults', array( |
| | 1191 | 'width' => $width, |
| | 1192 | 'height' => 700, |
| | 1193 | ) ); |
| | 1194 | } |
| | 1195 | |
| | 1196 | // Based on a supplied width/height example, return the biggest possible dimensions based on the max width/height |
| | 1197 | function wp_expand_dimensions( $example_width, $example_height, $max_width, $max_height ) { |
| | 1198 | return wp_constrain_dimensions( $example_width * 1000000, $example_height * 1000000, $max_width, $max_height ); |
| | 1199 | } |
| | 1200 | |
| | 1201 | // Wrapper to load the WP_oEmbed class and use it to get some HTML |
| | 1202 | function wp_oembed_get( $url, $args = '' ) { |
| | 1203 | require_once( 'class-oembed.php' ); |
| | 1204 | $oembed_object = _wp_oembed_get_object(); |
| | 1205 | return $oembed_object->getHTML( $url, $args ); |
| | 1206 | } |
| | 1207 | |
| | 1208 | |
| | 1209 | // YouTube internal handler (no oEmbed support) |
| | 1210 | wp_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' ); |
| | 1211 | function wp_embed_handler_youtube( $matches, $attr, $url, $rawattr ) { |
| | 1212 | if ( is_feed() ) |
| | 1213 | return "<a href='" . get_permalink() . "'><img src='http://img.youtube.com/vi/{$matches[3]}/0.jpg' alt='" . __('YouTube') . "' /></a>"; |
| | 1214 | |
| | 1215 | // If the user supplied a fixed width AND height, use it |
| | 1216 | if ( !empty($rawattr['width']) && !empty($rawattr['height']) ) { |
| | 1217 | $width = (int) $rawattr['width']; |
| | 1218 | $height = (int) $rawattr['height']; |
| | 1219 | } else { |
| | 1220 | list( $width, $height ) = wp_expand_dimensions( 425, 344, $attr['width'], $attr['height'] ); |
| | 1221 | } |
| | 1222 | |
| | 1223 | return apply_filters( 'embed_youtube', "<object width='$width' height='$height'><param name='movie' value='http://www.youtube.com/v/{$matches[3]}&hl=en&fs=1'></param><param name='allowFullScreen' value='true'></param><param name='allowscriptaccess' value='always'></param><embed src='http://www.youtube.com/v/{$matches[3]}&hl=en&fs=1' type='application/x-shockwave-flash' allowscriptaccess='always' allowfullscreen='true' width='$width' height='$height'></embed></object>", $matches, $attr, $url, $rawattr ); |
| | 1224 | } |
| | 1225 | |
| | 1226 | |
| | 1227 | // PollDaddy internal handler (no oEmbed support) |
| | 1228 | wp_embed_register_handler( 'polldaddy', '#http://answers.polldaddy.com/poll/(\d+)(.*?)#i', 'wp_embed_handler_polldaddy' ); |
| | 1229 | function wp_embed_handler_polldaddy( $matches, $attr, $url, $rawattr ) { |
| | 1230 | if ( is_feed() ) |
| | 1231 | return make_link($url); |
| | 1232 | |
| | 1233 | return apply_filters( 'embed_polldaddy', '<script type="text/javascript" src="http://s3.polldaddy.com/p/' . esc_attr($matches[1]) . '"></script>', $matches, $attr, $url, $rawattr ); |
| | 1234 | } |
| | 1235 | |
| | 1236 | |
| | 1237 | // DailyMotion internal handler (no oEmbed support) |
| | 1238 | // Example: http://www.dailymotion.com/video/x4cqyl_ferrari-p45-owner-exclusive-intervi_auto |
| | 1239 | wp_embed_register_handler( 'dailymotion', '#http://(www.dailymotion|dailymotion)\.com/(.+)/([0-9a-zA-Z]+)\_(.*?)#i', 'wp_embed_handler_dailymotion' ); |
| | 1240 | function wp_embed_handler_dailymotion( $matches, $attr, $url, $rawattr ) { |
| | 1241 | if ( is_feed() ) |
| | 1242 | return make_link($url); |
| | 1243 | |
| | 1244 | // If the user supplied a fixed width AND height, use it |
| | 1245 | if ( !empty($rawattr['width']) && !empty($rawattr['height']) ) { |
| | 1246 | $width = (int) $rawattr['width']; |
| | 1247 | $height = (int) $rawattr['height']; |
| | 1248 | } else { |
| | 1249 | list( $width, $height ) = wp_expand_dimensions( 480, 291, $attr['width'], $attr['height'] ); |
| | 1250 | } |
| | 1251 | |
| | 1252 | return apply_filters( 'embed_dailymotion', "<object width='$width' height='$height' classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000'><param name='movie' value='http://www.dailymotion.com/swf/{$matches[3]}&related=0'></param><param name='allowFullScreen' value='true'></param><param name='allowScriptAccess' value='always'></param><embed src='http://www.dailymotion.com/swf/{$matches[3]}&related=0' type='application/x-shockwave-flash' width='$width' height='$height' allowFullScreen='true' allowScriptAccess='always'></embed></object>", $matches, $attr, $url, $rawattr );; |
| | 1253 | } |