Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
wp-content
/
plugins
/
facebook
:
open-graph-protocol.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * Output Open Graph protocol for consumption by Facebook services building a summary of the webpage. * * @since 1.1 * * @link http://ogp.me/ Open Graph protocol */ class Facebook_Open_Graph_Protocol { /** * Base IRI of Open Graph protocol RDFa properties. * * @since 1.1 * * @var string */ const OGP_NS = 'http://ogp.me/ns#'; /** * Base IRI of Facebook RDFa properties. * * @since 1.1 * * @var string */ const FB_NS = 'http://ogp.me/ns/fb#'; /** * Base IRI of Open Graph protocol article object global properties. * * @since 1.1 * * @var string */ const ARTICLE_NS = 'http://ogp.me/ns/article#'; /** * Base IRI of Open Graph protocol profile object global properties. * * @since 1.1 * * @var string */ const PROFILE_NS = 'http://ogp.me/ns/profile#'; /** * Base IRI of Open Graph protocol video object global properties. * * @since 1.1 * * @var string */ const VIDEO_NS = 'http://ogp.me/ns/video#'; /** * Minimum edge of an acceptable Open Graph protocol image in whole pixels. * * @since 1.1.9 * * @var int */ const MIN_IMAGE_DIMENSION = 200; /** * Only list the first N eligible Open Graph protocol images. * * Facebook helps a person choose one of three parsed images when a link is pasted into a message * Other consumers of Open Graph protocol data (Google, Twitter, etc.) may index additional images; increase this number to trade-off speed for coverage * * @since 1.5 * * @var int */ const MAX_IMAGE_COUNT = 3; /** * Recursively build RDFa <meta> elements used for Open Graph protocol. * * @since 1.0 * * @param string $property whitespace separated list of CURIEs placed in a property attribute * @param mixed content attribute value for the given property. use an array for array property values or structured properties * @return void */ public static function meta_elements( $property, $content ) { if ( empty( $property ) || empty( $content ) ) return; // array of property values or structured property if ( is_array( $content ) ) { // account for the special structured property of url which is equivalent to the root tag and sets up the structure // must appear before other attributes if ( isset( $content['url'] ) ) { self::meta_elements( $property, $content['url'] ); unset( $content['url'] ); } foreach( $content as $structured_property => $content_value ) { // handle numeric keys from regular arrays if ( ! is_string( $structured_property ) ) self::meta_elements( $property, $content_value ); else self::meta_elements( $property . ':' . $structured_property, $content_value ); } } else { echo '<meta property="' . esc_attr( $property ) . '" content="' . esc_attr( $content ) . '"'; // do not use trailing slash if HTML5 // http://wiki.whatwg.org/wiki/FAQ#Should_I_close_empty_elements_with_.2F.3E_or_.3E.3F if ( ! current_theme_supports('html5') ) echo ' /'; echo ">\n"; } } /** * Clean post description text in preparation for Open Graph protocol description or Facebook post caption|description * * @since 1.1 * * @uses strip_shortcodes() * @uses wp_trim_words() * @param string $description description text * @return string description text passed through WordPress Core description cleaners, possibly trimmed */ public static function clean_description( $description, $trimmed = true ) { $description = trim( $description ); if ( ! $description ) return ''; // basic filters from wp_trim_excerpt() that should apply to both excerpt and content // note: the_content filter is so polluted with extra third-party stuff we purposely avoid to better represent page content $description = strip_shortcodes( $description ); // remove any shortcodes that may exist, such as an image macro $description = str_replace( ']]>', ']]>', $description ); $description = trim( $description ); if ( ! $description ) return ''; if ( $trimmed ) { // use the built-in WordPress function for localized support of words vs. characters // pass in customizations based on WordPress with publisher overrides $description = wp_trim_words( $description, apply_filters( 'facebook_excerpt_length', apply_filters( 'excerpt_length', 55 ) ), // standard excerpt length, which may be customized by the publisher via the core filter, passed through another filter for our context apply_filters( 'facebook_excerpt_more', __( '…' ) ) // filter the wp_trim_words default for publisher customization ); } else { $description = wp_strip_all_tags( $description ); } if ( $description ) return $description; return ''; } /** * Convert full IRI Open Graph protocol values to CURIE prefixed values * Mapping CURIEs is presumed to occur in a parent element of the group of <meta>s * * @since 1.1.6 * @link http://www.w3.org/TR/rdfa-syntax/#s_curies RDFa 1.1 Core CURIEs * @param array $ogp associative array of full IRI key and OGP value * @return array associative array of full IRI replaced with prefix where mapped and the same OGP value passed in */ public static function prefixed_properties( $ogp ) { global $facebook_loader; if ( ! is_array( $ogp ) || empty( $ogp ) ) return array(); /** * Map RDFa Core CURIEs and prefixes * * @since 1.1.6 * * @param array { * @type string RDFa CURIE * @type array 'prefix' abbreviation recognized without a prefix mapping on a parent element * } */ $curies = apply_filters( 'facebook_rdfa_mappings', array( self::OGP_NS => array( 'prefix' => 'og' ), self::FB_NS => array( 'prefix' => 'fb' ), self::ARTICLE_NS => array( 'prefix' => 'article' ), self::PROFILE_NS => array( 'prefix' => 'profile' ), 'http://ogp.me/ns/book#' => array( 'prefix' => 'book' ), 'http://ogp.me/ns/business#' => array( 'prefix' => 'business' ), 'http://ogp.me/ns/event#' => array( 'prefix' => 'event' ), 'http://ogp.me/ns/fitness#' => array( 'prefix' => 'fitness' ), 'http://ogp.me/ns/music#' => array( 'prefix' => 'music' ), 'http://ogp.me/ns/product#' => array( 'prefix' => 'product' ), 'http://ogp.me/ns/restaurant#' => array( 'prefix' => 'restaurant' ), self::VIDEO_NS => array( 'prefix' => 'video' ) ) ); if ( isset( $facebook_loader->credentials['app_namespace'] ) ) { $app_ns_url = esc_url_raw( 'http://ogp.me/ns/fb/' . $facebook_loader->credentials['app_namespace'] . '#', array( 'http' ) ); if ( $app_ns_url && ! isset( $curies[$app_ns_url] ) ) $curies[$app_ns_url] = array( 'prefix' => $facebook_loader->credentials['app_namespace'] ); } foreach ( $curies as $reference => $properties ) { if ( ! isset( $properties['prefix'] ) ) { unset( $curies[$reference] ); continue; } $properties['key_length'] = strlen( $reference ); $curies[$reference] = $properties; } $ogp_prefixed = array(); foreach ( $ogp as $property => $value ) { $prefixed_property_set = false; $property_length = strlen( $property ); foreach ( $curies as $reference => $properties ) { if ( $property_length > $properties['key_length'] && substr_compare( $property, $reference, 0, $properties['key_length'] ) === 0 ) { $ogp_prefixed[ $properties['prefix'] . ':' . substr( $property , $properties['key_length'] ) ] = $value; $prefixed_property_set = true; break; } } unset( $property_length ); // pass through unmapped values if ( ! $prefixed_property_set ) $ogp_prefixed[$property] = $value; unset( $prefixed_property_set ); } return $ogp_prefixed; } /** * Add Open Graph protocol markup to <head> * We use full IRIs for consistent mapping between mapped CURIE prefixes defined in a parent element and self-contained properties using a full IRI * * @since 1.0 * * @link http://www.w3.org/TR/rdfa-syntax/#s_curieprocessing RDFa Core 1.1 CURIE and IRI processing * @global stdClass|WP_Post $post WordPress post object for the current view * @global Facebook_Loader $facebook_loader * @return void */ public static function add_og_protocol() { global $post, $facebook_loader; $meta_tags = array( self::OGP_NS . 'site_name' => get_bloginfo( 'name' ), self::OGP_NS . 'type' => 'website' ); if ( isset( $facebook_loader ) ) { if ( isset( $facebook_loader->locale ) ) $meta_tags[ self::OGP_NS . 'locale' ] = $facebook_loader->locale; if ( isset( $facebook_loader->credentials ) && isset( $facebook_loader->credentials['app_id'] ) && $facebook_loader->credentials['app_id'] ) $meta_tags[ self::FB_NS . 'app_id' ] = $facebook_loader->credentials['app_id']; } if ( is_home() || is_front_page() ) { $meta_tags[ self::OGP_NS . 'title' ] = get_bloginfo( 'name' ); $meta_tags[ self::OGP_NS . 'description' ] = get_bloginfo( 'description' ); $meta_tags[ self::OGP_NS . 'url' ] = home_url(); } else if ( is_singular() && empty( $post->post_password ) ) { setup_postdata( $post ); $post_type = get_post_type( $post ); /** * Canonical URL for the singular page. * * @since 1.1.6 * @param string absolute URI of the current post context */ $meta_tags[ self::OGP_NS . 'url' ] = apply_filters( 'facebook_rel_canonical', get_permalink( $post->ID ) ); if ( post_type_supports( $post_type, 'title' ) ) $meta_tags[ self::OGP_NS . 'title' ] = get_the_title(); if ( post_type_supports( $post_type, 'excerpt' ) ) { $description = ''; // did the publisher specify a custom excerpt? use it if ( ! empty( $post->post_excerpt ) ) $description = apply_filters( 'get_the_excerpt', $post->post_excerpt ); else $description = $post->post_content; $description = self::clean_description( $description ); if ( $description ) $meta_tags[ self::OGP_NS . 'description' ] = $description; } $meta_tags[ self::OGP_NS . 'type' ] = self::get_post_og_type( $post ); $images = self::get_og_images( $post ); if ( ! empty( $images ) ) $meta_tags[ self::OGP_NS . 'image' ] = array_values( $images ); unset( $images ); if ( $meta_tags[ self::OGP_NS . 'type' ] === 'article' ) { // add article-specific properties $meta_tags = array_merge( $meta_tags, self::get_article_properties( $post ) ); } else if ( $meta_tags[ self::OGP_NS . 'type' ] === 'video.other' ) { // attempt to declare a video or videos $videos = self::get_og_videos( $post ); if ( ! empty( $videos ) ) { $videos = array_values( $videos ); if ( count( $videos ) === 1 ) { $video = array_shift( $videos ); if ( $video && isset( $video[ self::VIDEO_NS ] ) ) { foreach( $video[self::VIDEO_NS] as $property => $value ) { $meta_tags[ self::VIDEO_NS . $property ] = $value; } unset( $video[ self::VIDEO_NS ] ); } $meta_tags[ self::OGP_NS . 'video' ] = array( $video ); unset( $video ); } else { $videos_only = array(); foreach( $videos as $video ) { unset( $video[self::VIDEO_NS] ); $videos_only[] = $video; } $meta_tags[ self::OGP_NS . 'video' ] = $videos_only; unset( $videos_only ); } } unset( $videos ); if ( empty( $meta_tags[ self::OGP_NS . 'video' ] ) ) { // try to find well-known and easily mapped video provider WP_Embeds $videos = self::get_embed_videos( $post ); if ( ! empty( $videos ) ) { foreach( $videos as $video_url => $video ) { if ( empty( $video ) ) continue; if ( isset( $video['image'] ) ) { $meta_tags[ self::OGP_NS . 'image' ][] = $video['image']; unset( $video['image'] ); } $meta_tags[ self::OGP_NS . 'video' ] = array_values( $video ); } } unset( $videos ); } } // include MP3s for audio post formats if ( has_post_format( 'audio', $post ) ) { $audios = self::get_og_audio( $post ); if ( ! empty( $audios ) ) $meta_tags[ self::OGP_NS . 'audio' ] = array_values( $audios ); unset( $audios ); } if ( post_type_supports( $post_type, 'author' ) && isset( $post->post_author ) ) { // Facebook user helper if ( ! class_exists( 'Facebook_User' ) ) require_once( $facebook_loader->plugin_directory . 'facebook-user.php' ); $facebook_user_data = Facebook_User::get_user_meta( $post->post_author, 'fb_data', true ); $author_fbid = ''; if ( is_array( $facebook_user_data ) && isset( $facebook_user_data['fb_uid'] ) ) $author_fbid = $facebook_user_data['fb_uid']; unset( $facebook_user_data ); if ( $author_fbid ) { $meta_tags[ self::FB_NS . 'profile_id' ] = $author_fbid; // adding an fb:admin grants comment moderation permissions for Comment Box if ( get_option( 'facebook_comments_enabled' ) && user_can( $post->post_author, 'moderate_comments' ) ) { if ( ! class_exists( 'Facebook_Comments' ) ) require_once( $facebook_loader->plugin_directory . 'social-plugins/class-facebook-comments.php' ); if ( Facebook_Comments::comments_enabled_for_post_type( $post ) ) $meta_tags[ self::FB_NS . 'admins' ] = $author_fbid; } } unset( $author_fbid ); } } else if ( is_author() ) { $author = get_queried_object(); if ( $author && isset( $author->ID ) ) { $author_id = $author->ID; $meta_tags[ self::OGP_NS . 'type' ] = 'profile'; $meta_tags[ self::OGP_NS . 'title' ] = get_the_author_meta( 'display_name', $author_id ); $meta_tags[ self::OGP_NS . 'url' ] = get_author_posts_url( get_the_author_meta( 'ID', $author_id ) ); $meta_tags[ self::PROFILE_NS . 'first_name' ] = get_the_author_meta( 'first_name', $author_id ); $meta_tags[ self::PROFILE_NS . 'last_name'] = get_the_author_meta( 'last_name', $author_id ); $description = self::clean_description( get_the_author_meta( 'description', $author_id ) ); if ( $description ) $meta_tags[ self::OGP_NS . 'description'] = $description; // Facebook user helper if ( ! class_exists( 'Facebook_User' ) ) require_once( $facebook_loader->plugin_directory . 'facebook-user.php' ); $facebook_user_data = Facebook_User::get_user_meta( $author_id, 'fb_data', true ); if ( is_array( $facebook_user_data ) && isset( $facebook_user_data['fb_uid'] ) ) $meta_tags[ self::FB_NS . 'profile_id' ] = $facebook_user_data['fb_uid']; unset( $facebook_user_data ); // no need to show username if there is only one if ( is_multi_author() ) $meta_tags[ self::PROFILE_NS . 'username' ] = get_the_author_meta( 'login', $author_id ); } unset( $author ); } else if ( is_page() ) { $meta_tags[ self::OGP_NS . 'type' ] = 'article'; $meta_tags[ self::OGP_NS . 'title' ] = get_the_title(); // duplicate_hook $meta_tags[ self::OGP_NS . 'url' ] = apply_filters( 'facebook_rel_canonical', get_permalink() ); } /** * Customize Open Graph protocol for the site or page before output. * * Add, change, and delete Open Graph protocol values used to summarize a link shared on Facebook and create new Open Graph objects. * * @since 1.0 * @param array $meta_tags { * Open Graph protocol values to be output * * @type string Full IRI RDFa Core property * @type mixed property value or multiple values * } * @param stdClass|WP_Post $post the current post global if set */ $meta_tags = apply_filters( 'fb_meta_tags', $meta_tags, $post ); /** * Control auto-prefixing or full IRI RDFa Core 1.1 properties output for the page. * * Use prefixed values (e.g. og:image) by default to maximize compatibility with indexers. Assumes prefix CURIE mappings will be set on <head> or <html> to properly map the prefixes or the publisher is not concerned with RDFa Core 1.1 compatibility and reserved prefixes. * * @since 1.1.6 * @param bool default true */ if ( apply_filters( 'facebook_ogp_prefixed', true ) ) $meta_tags = self::prefixed_properties( $meta_tags ); foreach ( $meta_tags as $property => $content ) { self::meta_elements( $property, $content ); } } /** * Classify the current post as an Open Graph object type. * * @since 1.5 * * @link https://developers.facebook.com/docs/reference/opengraph/object-type/ Facebook global object types * @param stdClass|WP_Post $post the post to classify * @return string Open Graph protocol type property value */ public static function get_post_og_type( $post ) { $og_type = 'website'; if ( ! ( $post && isset( $post->ID ) ) ) return $og_type; // treat video post format as OG type video if ( has_post_format( 'video', $post ) ) $og_type = 'video.other'; else if ( get_post_type( $post ) === 'post' ) $og_type = 'article'; $og_type = apply_filters( 'facebook_og_type', $og_type, $post ); if ( ! $og_type ) $og_type = 'website'; return $og_type; } /** * Map WordPress post data to Open Graph article object properties. * * @since 1.5 * * @param stdClass|WP_Post $post the post of interest * @return array { * Associative array of OGP properties and values related to the post * * @type string Full IRI Open Graph protocol property * @type string|array Open Graph protocol property value * } */ public static function get_article_properties( $post ) { global $facebook_loader; $ogp = array( self::ARTICLE_NS . 'published_time' => date( 'c', strtotime( $post->post_date_gmt ) ), self::ARTICLE_NS . 'modified_time' => date( 'c', strtotime( $post->post_modified_gmt ) ) ); if ( post_type_supports( get_post_type( $post ), 'author' ) && isset( $post->post_author ) ) { if ( ! class_exists( 'Facebook_User' ) ) require_once( $facebook_loader->plugin_directory . 'facebook-user.php' ); $facebook_user_data = Facebook_User::get_user_meta( $post->post_author, 'fb_data', true ); if ( is_array( $facebook_user_data ) && isset( $facebook_user_data['link'] ) ) $ogp[ self::ARTICLE_NS . 'author' ] = $facebook_user_data['link']; else $ogp[ self::ARTICLE_NS . 'author' ] = get_author_posts_url( $post->post_author ); unset( $facebook_user_data ); } $facebook_page = get_option( 'facebook_publish_page' ); if ( is_array( $facebook_page ) && isset( $facebook_page['link'] ) ) $ogp[ self::ARTICLE_NS . 'publisher' ] = $facebook_page['link']; unset( $facebook_page ); // add the first category as a section. all other categories as tags $cat_ids = get_the_category( $post->ID ); if ( ! empty( $cat_ids ) ) { $no_category = apply_filters( 'the_category', __( 'Uncategorized' ) ); $cat = get_category( $cat_ids[0] ); if ( ! empty( $cat ) && isset( $cat->name ) && $cat->name !== $no_category ) $ogp[ self::ARTICLE_NS . 'section' ] = $cat->name; // output the rest of the categories as tags unset( $cat_ids[0] ); if ( ! empty( $cat_ids ) ) { $ogp[ self::ARTICLE_NS . 'tag' ] = array(); foreach( $cat_ids as $cat_id ) { $cat = get_category( $cat_id ); if ( isset( $cat->name ) && $cat->name !== $no_category ) $ogp[ self::ARTICLE_NS . 'tag' ][] = $cat->name; unset( $cat ); } } } // add tags. treat tags as lower priority than multiple categories $tags = get_the_tags( $post->ID ); if ( $tags ) { if ( ! isset( $ogp[ self::ARTICLE_NS . 'tag' ] ) ) $ogp[ self::ARTICLE_NS . 'tag' ] = array(); foreach ( $tags as $tag ) { $ogp[ self::ARTICLE_NS . 'tag' ][] = $tag->name; } } return $ogp; } /** * Extract Open Graph protocol image information from a WordPress image attachment. * * @since 1.5 * * @uses wp_get_attachment_image_src() * @param int $attachment_id post id of the attachment object * @param string $size requested size. one of thumbnail, medium, large, or full * @return array { * Attachment converted into image, width, and height values or empty if no image data found or minimum requirements not met * * @type string 'url' Full URI of the attachment for the requested size * @type int 'width' Width of the image in whole pixels * @type int 'height' Height of the image in whole pixels * } */ public static function attachment_to_og_image( $attachment_id, $size = 'full' ) { if ( ! ( is_string( $size ) && $size ) ) $size = 'full'; $image = array(); list( $attachment_url, $attachment_width, $attachment_height ) = wp_get_attachment_image_src( $attachment_id, $size ); if ( empty( $attachment_url ) ) return $image; $image['url'] = $attachment_url; if ( ! empty( $attachment_width ) ) { $image['width'] = absint( $attachment_width ); if ( ! $image['width'] ) unset( $image['width'] ); else if ( $image['width'] < self::MIN_IMAGE_DIMENSION ) return array(); } if ( ! empty( $attachment_height ) ) { $image['height'] = absint( $attachment_height ); if ( ! $image['height'] ) unset( $image['height'] ); else if ( $image['height'] < self::MIN_IMAGE_DIMENSION ) return array(); } return $image; } /** * Identify image attachments related to the post. * * Request the full size of the attachment, not necessarily the same as the size used in the post. * * @since 1.5 * * @param stdClass|WP_Post $post WordPress post of interest * @return array { * Open Graph protocol image structured data * * @type string URL of the image * @type array associative array of image data * } */ public static function get_og_images( $post ) { $og_images = array(); if ( ! $post || ! isset( $post->ID ) ) return $og_images; // does current post type and the current theme support post thumbnails? if ( post_type_supports( get_post_type( $post ), 'thumbnail' ) && function_exists( 'has_post_thumbnail' ) && has_post_thumbnail() ) { list( $post_thumbnail_url, $post_thumbnail_width, $post_thumbnail_height ) = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' ); $image = self::attachment_to_og_image( get_post_thumbnail_id( $post->ID ) ); if ( isset( $image['url'] ) && ! isset( $og_images[ $image['url'] ] ) ) $og_images[ $image['url'] ] = $image; unset( $image ); } // get image attachments if ( function_exists( 'get_attached_media' ) ) { $images = get_attached_media( 'image', $post->ID ); if ( ! empty( $images ) ) { foreach( $images as $i ) { if ( ! isset( $i->ID ) ) continue; $image = self::attachment_to_og_image( $i->ID ); if ( ! isset( $image['url'] ) || isset( $og_images[ $image['url'] ] ) ) continue; $og_images[ $image['url'] ] = $image; if ( count( $og_images ) === self::MAX_IMAGE_COUNT ) return $og_images; } } unset( $images ); } // test for WP_Embed handled images if ( ! empty( $post->post_content ) ) { // Instagram preg_match_all( '#\s*http://instagr(\.am|am\.com)/p/(.*)/\s*#i', $post->post_content, $matches ); if ( isset( $matches[2] ) ) { foreach( $matches[2] as $instagram_id ) { $instagram_url = esc_url_raw( 'http://instagram.com/p/' . $instagram_id . '/media/?size=l', array( 'http' ) ); if ( ! $instagram_url || isset( $og_images[$instagram_url] ) ) continue; $og_images[$instagram_url] = array( 'url' => $instagram_url ); unset( $instagram_url ); } } unset( $matches ); } // add gallery content if ( function_exists( 'get_post_galleries' ) ) { // use get_post_galleries function from WP 3.6+ $galleries = get_post_galleries( $post, false ); foreach( $galleries as $gallery ) { // use ids to request full-size image URI if ( ! ( isset( $gallery['ids'] ) && $gallery['ids'] ) ) continue; $gallery['ids'] = explode( ',', $gallery['ids'] ); foreach( $gallery['ids'] as $attachment_id ) { $image = self::attachment_to_og_image( $attachment_id ); if ( ! isset( $image['url'] ) || isset( $og_images[ $image['url'] ] ) ) continue; $og_images[ $image['url'] ] = $image; if ( count( $og_images ) === self::MAX_IMAGE_COUNT ) return $og_images; unset( $image ); } } unset( $galleries ); } else { // extract full-size images from gallery shortcode $og_images = self::gallery_images( $post, $og_images ); } return $og_images; } /** * Identify video attachments related to the post. * * Prefer SWF and MP4 videos. * * @since 1.5 * * @param stdClass|WP_Post $post WordPress post of interest * @return array { * Array of associative arrays containing OGP video structured data * * @type string URL of the video * @type array Open Graph protocol video properties with possible video object namespaced properties * } */ public static function get_og_videos( $post ) { $og_videos = array(); if ( ! $post ) return $og_videos; if ( function_exists( 'get_attached_media' ) ) { $videos = get_attached_media( 'video', $post->ID ); foreach( $videos as $video ) { if ( ! isset( $video->ID ) ) continue; $url = wp_get_attachment_url( $video->ID ); if ( ! $url || isset( $og_videos[ $url ] ) ) continue; $og_video = array( 'url' => $url ); unset( $url ); if ( isset( $video->post_mime_type ) && $video->post_mime_type ) $og_video['type'] = $video->post_mime_type; $meta = wp_get_attachment_metadata( $video->ID ); if ( is_array( $meta ) ) { if ( isset( $meta['width'] ) && isset( $meta['height'] ) ) { $meta['width'] = absint( $meta['width'] ); $meta['height'] = absint( $meta['height'] ); if ( $meta['width'] < self::MIN_IMAGE_DIMENSION || $meta['height'] < self::MIN_IMAGE_DIMENSION ) continue; $og_video['width'] = $meta['width']; $og_video['height'] = $meta['height']; } if ( isset( $meta['length'] ) ) { $meta['length'] = absint( $meta['length'] ); if ( $meta['length'] > 0 ) $og_video[ self::VIDEO_NS ]['duration'] = $meta['length']; } } unset( $meta ); $og_videos[ $og_video['url'] ] = $og_video; unset( $og_video ); } unset( $videos ); } return $og_videos; } /** * Convert popular video providers to SWF and image URLs * * @since 1.5.3 * * @param stdClass|WP_Post $post WordPress post of interest * @return array { * Array of associative arrays containing OGP video structured data for WP_Embed autolinked providers * * @type string URL of the video * @type array Open Graph protocol video properties with possible 'image' key containing an og:image strutured data array for the video * } */ public static function get_embed_videos( $post ) { $og_videos = array(); if ( ! ( isset( $post->post_content ) && $post->post_content ) ) return $og_videos; // @see WP_Embed::autoembed() preg_match_all( '|^\s*(https?://[^\s"]+)\s*$|im', $post->post_content, $embed_urls ); if ( ! isset( $embed_urls[1] ) ) return $og_videos; $embed_urls = array_unique( $embed_urls[1], SORT_STRING ); foreach( $embed_urls as $embed_url ) { if ( preg_match( '#^https?://www\.youtube\.com/watch\?(.*)#i', $embed_url, $matches ) ) { parse_str( $matches[1], $youtube_parameters ); if ( isset( $youtube_parameters['v'] ) && $youtube_parameters['v'] ) $og_videos = self::youtube_video_to_open_graph( $youtube_parameters['v'], $og_videos ); unset( $youtube_parameters ); } else if ( preg_match( '#^http://youtu\.be/(.*)#i', $embed_url, $matches ) ) { $og_videos = self::youtube_video_to_open_graph( $matches[1], $og_videos ); } else if ( preg_match( '#^https?://(www\.)?vimeo\.com/([0-9]+)\??#i', $embed_url, $matches ) ) { $og_videos = self::vimeo_video_to_open_graph( $matches[2], $og_videos ); } } return $og_videos; } /** * Identify MP3 (audio/mpeg) attachments related to the post. * * @since 1.5 * * @param stdClass|WP_Post $post WordPress post of interest * @return array { * Array of associative arrays containing OGP audio structured data * * @type string URL of the audio file * @type array Open Graph protocol audio properties * } */ public static function get_og_audio( $post ) { $og_audios = array(); if ( ! $post ) return $og_audios; if ( function_exists( 'get_attached_media' ) ) { $audios = get_attached_media( 'audio/mpeg', $post->ID ); foreach ( $audios as $audio ) { $url = wp_get_attachment_url( $audio->ID ); if ( ! $url || isset( $og_audios[ $url ] ) ) continue; $og_audio = array( 'url' => $url ); unset( $url ); if ( isset( $audio->post_mime_type ) && $audio->post_mime_type ) $og_audio['type'] = $audio->post_mime_type; $og_audios[ $og_audio['url'] ] = $og_audio; unset( $og_audio ); } unset( $audios ); } return $og_audios; } /** * Find gallery shortcodes in the post. Build Open Graph protocol image results. * * Used if get_post_galleries() is not present (adds support for WordPress < 3.6). * * @since 1.1.9 * * @param stdClass|WP_Post $post current post object * @param array $existing_images add to an existing list of images. Check for duplication. * @return array { * Array of arrays containing Open Graph protocol image markup * * @type string URL of the image * @type array Open Graph protocol image properties * } */ public static function gallery_images( $post, $existing_images = array() ) { global $shortcode_tags; if ( ! is_array( $existing_images ) ) $existing_images = array(); $og_images = array(); if ( ! ( isset( $shortcode_tags['gallery'] ) && isset( $post->post_content ) && $post->post_content ) ) return $og_images; $first_gallery = strpos( $post->post_content, '[gallery' ); if ( $first_gallery === false ) return $og_images; // use regex finder with only the gallery shortcode $old_shortcodes = $shortcode_tags; $shortcode_tags = array( 'gallery' => $shortcode_tags['gallery'] ); // find all the gallery shortcodes in the post content preg_match_all( '/' . get_shortcode_regex() . '/s', $post->post_content, $galleries, PREG_SET_ORDER, $first_gallery ); // reset $shortcode_tags = $old_shortcodes; foreach( $galleries as $gallery_shortcode ) { // request the full-sized image if ( empty( $gallery_shortcode[3] ) ) { $gallery_shortcode[3] = ' size="full"'; } else { // break it down $parsed_attributes = shortcode_parse_atts( $gallery_shortcode[3] ); // add new attribute or override existing $parsed_attributes['size'] = 'full'; // build it up again $attr_str = ''; foreach ( $parsed_attributes as $attribute => $value ) { $attr_str .= ' ' . $attribute . '="' . $value . '"'; } unset( $parsed_attributes ); if ( $attr_str ) $gallery_shortcode[3] = $attr_str; unset( $attr_str ); } // pass the shortcode through all filters and actors $gallery_html = do_shortcode_tag( $gallery_shortcode ); if ( ! $gallery_html ) continue; $gallery_html = strip_tags( $gallery_html, '<img>' ); if ( ! $gallery_html ) continue; $first_image = strpos( $gallery_html, '<img' ); if ( $first_image === false ) continue; preg_match_all( '/<img[^>]+>/i', $gallery_html, $images, PREG_PATTERN_ORDER, $first_image ); unset( $gallery_html ); if ( ! ( is_array( $images ) && is_array( $images[0] ) ) ) continue; $images = $images[0]; foreach ( $images as $image ) { preg_match_all( '/(src|width|height)="([^"]*)"/i', $image, $image_attributes, PREG_SET_ORDER ); $og_image = array(); foreach( $image_attributes as $parsed_attributes ) { if ( $parsed_attributes[1] === 'src' ) { $url = esc_url_raw( wp_specialchars_decode( $parsed_attributes[2] ), array( 'http', 'https' ) ); if ( $url ) $og_image['url'] = $url; unset( $url ); } else if ( $parsed_attributes[1] === 'width' || $parsed_attributes[1] === 'height' ) { $pixels = absint( $parsed_attributes[2] ); if ( $pixels > 0 ) $og_image[ $parsed_attributes[1] ] = $pixels; unset( $pixels ); } } if ( ! isset( $og_image['url'] ) || isset( $existing_images[ $og_image['url'] ] ) || ( isset( $og_image['width'] ) && $og_image['width'] < self::MIN_IMAGE_DIMENSION ) || ( isset( $og_image['height'] ) && $og_image['height'] < self::MIN_IMAGE_DIMENSION ) ) continue; $existing_images[ $og_image['url'] ] = $og_image; unset( $og_image ); if ( count( $existing_images ) === self::MAX_IMAGE_COUNT ) return $existing_images; } } return $existing_images; } /** * Build Open Graph protocol markup for a single YouTube video identifier. * * @since 1.5.3 * * @link https://developers.google.com/youtube/player_parameters YouTube Embedded Player Parameters * @param string $youtube_id YouTube video identifier * @param array $exiting_videos Existing matched videos. Avoid duplication by comparing YouTube HTML embed URIs * @return array Passed 'existing_videos' array with a possible YouTube video added */ public static function youtube_video_to_open_graph( $youtube_id, $og_videos = array() ) { if ( ! is_array( $og_videos ) ) $og_videos = array(); if ( ! ( is_string( $youtube_id ) && $youtube_id ) ) return $og_videos; $iframe_url = esc_url_raw( 'https://www.youtube.com/embed/' . $youtube_id . '?autoplay=1&rel=0', array( 'https' ) ); if ( ! $iframe_url || isset( $og_videos[ $iframe_url ] ) ) return $existing_videos; $flash_url = esc_url_raw( 'https://www.youtube.com/v/' . $youtube_id . '?version=3&autoplay=1&rel=0', array( 'https' ) ); if ( ! $flash_url ) return $og_videos; $og_videos[ $iframe_url ] = array( 'html' => array( 'url' => $iframe_url, 'type' => 'text/html' ), 'swf' => array( 'url' => $flash_url, 'type' => 'application/x-shockwave-flash' ) ); $image_url = esc_url_raw( 'http://img.youtube.com/vi/' . $youtube_id . '/sddefault.jpg', array( 'http', 'https' ) ); if ( $image_url ) { $og_videos[ $iframe_url ]['image'] = array( 'url' => $image_url ); } return $og_videos; } /** * Build Open Graph protocol markup for a single Vimeo video identifier. * * Note: this function does not query the Vimeo API and therefore does not include a thumbnail image for the video. For best results be sure to set your own og:image. * * @since 1.5.3 * * @link http://developer.vimeo.com/player/embedding Vimeo Embedded Player documentation * @param string $vimeo_id Vimeo video identifier * @param array $exiting_videos Existing matched videos. Avoid duplication by comparing Vimeo HTML embed URIs * @return array Passed 'existing_videos' array with a possible Vimeo video added */ public static function vimeo_video_to_open_graph( $vimeo_id, $og_videos = array() ){ if ( ! is_array( $og_videos ) ) $og_videos = array(); if ( ! ( is_string( $vimeo_id ) && $vimeo_id ) ) return $og_videos; $iframe_url = esc_url_raw( 'https://player.vimeo.com/video/' . $vimeo_id . '?autoplay=1', array( 'https' ) ); if ( ! $iframe_url || isset( $og_videos[ $iframe_url ] ) ) return $og_videos; $flash_url = esc_url_raw( 'https://vimeo.com/moogaloop.swf?' . http_build_query( array( 'clip_id' => $vimeo_id, 'autoplay' => 1 ), '', '&' ), array( 'https' ) ); if ( ! $flash_url ) return $og_videos; $og_videos[ $iframe_url ] = array( 'html' => array( 'url' => $iframe_url, 'type' => 'text/html' ), 'swf' => array( 'url' => $flash_url, 'type' => 'application/x-shockwave-flash' ) ); return $og_videos; } } ?>