'img', 'pid_attr' => 'data-[a-z]+-pid', ); private static $image_src_args = null; public function __construct( &$plugin ) { $this->p =& $plugin; if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } require_once WPSSO_PLUGINDIR . 'lib/media-filters.php'; $this->filters = new WpssoMediaFilters( $plugin ); add_action( 'init', array( $this, 'allow_img_data_attributes' ) ); add_action( 'post-upload-ui', array( $this, 'show_post_upload_ui_message' ) ); add_filter( 'editor_max_image_size', array( $this, 'maybe_adjust_max_image_size' ), 10, 3 ); add_filter( 'image_make_intermediate_size', array( $this, 'maybe_update_intermediate_size_filepath' ), -5000, 1 ); add_filter( 'wp_image_resize_identical_dimensions', array( $this, 'maybe_resize_fuzzy_dimensions' ), PHP_INT_MAX, 1 ); add_filter( 'wp_get_attachment_image_attributes', array( $this, 'add_attachment_image_attributes' ), 10, 2 ); add_filter( 'get_image_tag', array( $this, 'get_image_tag' ), 10, 6 ); } public function allow_img_data_attributes() { global $allowedposttags; $allowedposttags[ 'img' ][ 'data-wp-pid' ] = true; } public function show_post_upload_ui_message() { $msg_transl = '' . __( 'Minimum image dimension to satisfy all %1$d image sizes: %2$dpx width by %3$dpx height.', 'wpsso' ) . ''; list( $min_width, $min_height, $size_count ) = SucomUtilWP::get_minimum_image_wh(); echo '
'; echo sprintf( $msg_transl, $size_count, $min_width, $min_height ); echo '
' . "\n"; /* * Hide the "suggested image dimensions" as they are less than the minimum we suggest here. */ echo '' . "\n"; } /* * Note that $size can be a string or an array(). */ public function maybe_adjust_max_image_size( $max_image_size = array(), $size = '', $context = '' ) { /* * Allow our sizes to exceed the editor width. */ if ( is_string( $size ) && 0 === strpos( $size, 'wpsso-' ) ) { $max_image_size = array( 0, 0 ); } return $max_image_size; } /* * By default, WordPress adds only the resolution of the resized image to the file name. If the image size is ours, * then add crop information to the file name as well. This allows for different cropped versions for the same * image resolution. * * Example: * * unicorn-wallpaper-1200x628.jpg * unicorn-wallpaper-1200x628-cropped.jpg * unicorn-wallpaper-1200x628-cropped-center-top.jpg */ public function maybe_update_intermediate_size_filepath( $filepath ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } /* * get_attachment_image_src() in the WpssoMedia class saves / sets the image information (pid, size_name, * etc) before calling the image_make_intermediate_size() function (and others). Returns null if no image * information was set (presumably because we arrived here without passing through our own method). */ $img_src_args = self::get_image_src_args(); if ( empty( $img_src_args[ 'size_name' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'skipping ' . $filepath . ': size name is empty' ); } return $filepath; } elseif ( 0 !== strpos( $img_src_args[ 'size_name' ], 'wpsso-' ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'skipping ' . $filepath . ': size name is ' . $img_src_args[ 'size_name' ] ); } return $filepath; } /* * If the resized image is not cropped, then leave the file name as-is. */ $size_is_cropped = $this->p->util->is_size_cropped( $img_src_args[ 'size_name' ], $img_src_args[ 'pid' ] ); if ( ! $size_is_cropped ) { return $filepath; } /* * Example $size_info = Array ( * [size_name] => wpsso-opengraph, * [attachment_id] => false, * [width] => 1200, * [height] => 630, * [crop] => 1, * [is_cropped] => 1, * [dims_transl] => 1200x630 cropped, * [label_transl] => Open Graph (Facebook and oEmbed), * [opt_prefix] => og, * ); */ $size_info = $this->p->util->get_size_info( $img_src_args[ 'size_name' ], $img_src_args[ 'pid' ] ); // Uses a local static cache. $new_filepath = $this->maybe_update_cropped_image_filepath( $filepath, $size_info ); if ( $filepath !== $new_filepath ) { // Just in case /* * Determine if an image can be renamed or needs to be copied. * * Check for conflicting image sizes (ie. same dimensions uncropped, or same dimensions from other WordPress sizes). */ $can_rename = $this->can_rename_image_filename( $img_src_args[ 'size_name' ], $img_src_args[ 'pid' ] ); if ( $can_rename ) { if ( rename( $filepath, $new_filepath ) ) { // No other plugin/theme uses the same dimensions. return $new_filepath; // Return the new file path on success. } elseif ( copy( $filepath, $new_filepath ) ) { // If rename failed, then copy and delete. if ( unlink( $filepath ) ) { // Remove the old file path. return $new_filepath; // Return the new file path on success. } else { // If deleting the original failed, then never mind. :) unlink( $new_filepath ); } } } elseif ( copy( $filepath, $new_filepath ) ) { return $new_filepath; // Return the new file path on successful copy. } } return $filepath; } /* * Hooked to the 'wp_image_resize_identical_dimensions' filter added in WP v5.3. */ public function maybe_resize_fuzzy_dimensions( $resize ) { $img_src_args = self::get_image_src_args(); if ( empty( $img_src_args[ 'size_name' ] ) || 0 !== strpos( $img_src_args[ 'size_name' ], 'wpsso-' ) ) { return $resize; } return true; } /* * $attr = apply_filters( 'wp_get_attachment_image_attributes', $attr, $attachment ); */ public function add_attachment_image_attributes( $attr, $attachment ) { $attr[ 'data-wp-pid' ] = $attachment->ID; return $attr; } /* * $html = apply_filters( 'get_image_tag', $html, $id, $alt, $title, $align, $size ); */ public function get_image_tag( $html, $id, $alt, $title, $align, $size ) { $html = SucomUtil::insert_html_tag_attributes( $html, array( 'data-wp-pid' => $id ) ); return $html; } public function get_all_previews( $num, array $mod, $md_pre = 'og', $force_prev = false ) { /* * The get_all_videos() method uses the 'og_vid_max' argument as part of its caching salt, so re-use the * original number to get all possible videos (from its cache), then maybe limit the number of preview * images if necessary. */ $max_nums = $this->p->util->get_max_nums( $mod ); $mt_videos = $this->get_all_videos( $max_nums[ 'og_vid_max' ], $mod, $md_pre, $force_prev ); $this->p->util->clear_uniq_urls( $uniq_context = array( 'preview' ), $mod ); $mt_images = array(); foreach ( $mt_videos as $num => $mt_single_video ) { $image_url = SucomUtil::get_first_mt_media_url( $mt_single_video ); /* * Check preview images for duplicates since the same videos may be available in different formats * (application/x-shockwave-flash and text/html for example). */ if ( $image_url ) { if ( $this->p->util->is_uniq_url( $image_url, $uniq_context = 'preview', $mod ) ) { $mt_single_image = SucomUtil::preg_grep_keys( '/^og:image/', $mt_single_video ); if ( $this->p->util->push_max( $mt_images, $mt_single_image, $num ) ) { return $mt_images; } } } unset( $mt_videos[ $num ] ); } return $mt_images; } /* * Returns an array of single video associative arrays. */ public function get_all_videos( $num, array $mod, $md_pre = 'og', $force_prev = false ) { $cache_salt = array( 'num' => $num, 'mod' => $mod, 'md_pre' => $md_pre, 'force_prev' => $force_prev, ); if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'getting all videos' ); // Begin timer. $this->p->debug->log_args( $cache_salt ); } static $local_fifo = array(); $cache_id = md5( SucomUtil::get_array_pretty( $cache_salt, $flatten = true ) ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'cache id = ' . $cache_id ); } if ( isset( $local_fifo[ $cache_id ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'returning video data from local cache' ); $this->p->debug->mark( 'getting all videos' ); // End timer. } return $local_fifo[ $cache_id ]; } /* * Maybe limit the number of array elements. */ $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX ); $local_fifo[ $cache_id ] = array(); $mt_videos =& $local_fifo[ $cache_id ]; // Set reference variable. $this->p->util->clear_uniq_urls( array( 'video', 'video_details' ), $mod ); $add_vid_prev = empty( $this->p->options[ 'og_vid_prev_img' ] ) ? false : true; // Change value from 0/1 to false/true. if ( $this->p->debug->enabled ) { $this->p->debug->log( '$add_vid_prev is ' . ( $add_vid_prev ? 'true' : 'false' ) ); } $num_diff = SucomUtil::array_count_diff( $mt_videos, $num ); /* * Get video information and preview enable/disable option from the post/term/user meta. */ if ( is_object( $mod[ 'obj' ] ) && $mod[ 'id' ] ) { /* * Note that get_options() returns null if an index key is not found. */ if ( ( $mod_vid_prev = $mod[ 'obj' ]->get_options( $mod[ 'id' ], 'og_vid_prev_img' ) ) !== null ) { // Returns null, 0, or 1. if ( $this->p->debug->enabled ) { $this->p->debug->log( '$mod_vid_prev is ' . ( $mod_vid_prev ? 'true' : 'false' ) ); } $add_vid_prev = empty( $mod_vid_prev ) ? false : true; // Change value from 0/1 to false/true. } if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'checking for videos in ' . $mod[ 'name' ] . ' options' ); // Begin timer. } /* * get_og_videos() converts the $md_pre value to an array and always checks for 'og' metadata as a fallback. */ $mt_videos = array_merge( $mt_videos, $mod[ 'obj' ]->get_og_videos( $num_diff, $mod[ 'id' ], $md_pre ) ); if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'checking for videos in ' . $mod[ 'name' ] . ' options' ); // End timer. } } $num_diff = SucomUtil::array_count_diff( $mt_videos, $num ); /* * Maybe get more videos from the comment or post content. */ if ( $mod[ 'is_comment' ] || $mod[ 'is_post' ] ) { if ( ! $this->p->util->is_maxed( $mt_videos, $num ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'checking for videos in the ' . $mod[ 'name' ] . ' content' ); // Begin timer. } $mt_videos = array_merge( $mt_videos, $this->get_content_videos( $num_diff, $mod ) ); if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'checking for videos in the ' . $mod[ 'name' ] . ' content' ); // End timer. } } } $this->p->util->slice_max( $mt_videos, $num ); // Maybe trim the $mt_videos array. /* * Maybe remove the image meta tags (aka video preview). */ if ( empty( $add_vid_prev ) && empty( $force_prev ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'removing video preview images: $add_vid_prev and $force_prev are both false' ); } foreach ( $mt_videos as &$mt_single_video ) { // Uses reference. $mt_single_video = SucomUtil::preg_grep_keys( '/^og:image/', $mt_single_video, $invert = true ); $mt_single_video[ 'og:video:has_image' ] = false; } } /* * Get custom video information from post/term/user meta data for the FIRST video. * * If $md_pre is 'none' (special index keyword), then don't load any custom video information. * * The og:video:title and og:video:description meta tags are not standard and their values will only appear in Schema markup. */ if ( is_object( $mod[ 'obj' ] ) && $mod[ 'id' ] && $md_pre !== 'none' ) { foreach ( $mt_videos as &$mt_single_video ) { // Uses reference. foreach ( array( 'og_vid_title' => 'og:video:title', 'og_vid_desc' => 'og:video:description', 'og_vid_stream_url' => 'og:video:stream_url', // VideoObject contentUrl. 'og_vid_width' => 'og:video:width', 'og_vid_height' => 'og:video:height', 'og_vid_upload' => 'og:video:upload_date', ) as $md_key => $mt_name ) { if ( 'og_vid_upload' === $md_key ) { if ( ! empty( $mt_single_video[ 'og:video:upload_date' ] ) ) { /* * Use the existing upload date, time, and timezone as defaults. */ $value = WpssoSchema::get_mod_date_iso( $mod, $md_key, $mt_single_video[ 'og:video:upload_date' ] ); } else $value = WpssoSchema::get_mod_date_iso( $mod, $md_key ); } else { /* * Note that get_options() returns null if an index key is not found. */ $value = $mod[ 'obj' ]->get_options( $mod[ 'id' ], $md_key ); } if ( ! empty( $value ) ) { // Must be a non-empty string. $mt_single_video[ $mt_name ] = $value; } } break; // Only do the first video. } } if ( $this->p->debug->enabled ) { $this->p->debug->log( 'creating $mt_extend array' ); } $mt_extend = array(); foreach ( $mt_videos as &$mt_single_video ) { // Uses reference. if ( ! is_array( $mt_single_video ) ) { // Just in case. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'video ignored: $mt_single_video is not an array' ); } continue; } if ( $this->p->debug->enabled ) { $this->p->debug->log( '' ); } if ( ! empty( $mt_single_video[ 'og:video:embed_url' ] ) && 'text/html' !== $mt_single_video[ 'og:video:type' ] ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'adding og:video:type text/html for ' . $mt_single_video[ 'og:video:embed_url' ] ); } /* * Start with a fresh copy of all og meta tags. */ $og_single_embed = SucomUtil::get_mt_video_seed( 'og', $mt_single_video, false ); /* * Use only og meta tags, excluding the facebook applink meta tags. */ $og_single_embed = SucomUtil::preg_grep_keys( '/^og:/', $og_single_embed ); unset( $og_single_embed[ 'og:video:secure_url' ] ); // Just in case. $og_single_embed[ 'og:video:url' ] = $mt_single_video[ 'og:video:embed_url' ]; $og_single_embed[ 'og:video:has_video' ] = true; // Used by video API modules. $og_single_embed[ 'og:video:type' ] = 'text/html'; /* * Embedded videos may not have width / height information defined. */ foreach ( array( 'og:video:width', 'og:video:height' ) as $mt_name ) { if ( isset( $og_single_embed[ $mt_name ] ) && $og_single_embed[ $mt_name ] === '' ) { unset( $og_single_embed[ $mt_name ] ); } } /* * Add application/x-shockwave-flash video first and the text/html video second. */ if ( SucomUtil::get_first_mt_media_url( $mt_single_video, $mt_media_pre = 'og:video', $mt_suffixes = array( ':secure_url', ':url', '' ) ) ) { $mt_extend[] = $mt_single_video; } $mt_extend[] = $og_single_embed; } else $mt_extend[] = $mt_single_video; } if ( $this->p->debug->enabled ) { $this->p->debug->log( 'returning ' . count( $mt_extend ) . ' videos' ); $this->p->debug->log_arr( 'mt_extend', $mt_extend ); $this->p->debug->mark( 'getting all videos' ); // End timer. } /* * Update the local static cache and return the videos array. */ return $mt_videos = $mt_extend; } /* * $size_names can be a keyword (ie. 'opengraph' or 'schema'), a registered size name, or an array of size names. */ public function get_thumbnail_url( $size_names, array $mod, $md_pre = 'og' ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $mt_ret = $this->get_all_images( $num = 1, $size_names, $mod, $md_pre ); return SucomUtil::get_first_mt_media_url( $mt_ret ); } /* * $size_names can be a keyword (ie. 'opengraph' or 'schema'), a registered size name, or an array of size names. */ public function get_all_images( $num, $size_names, array $mod, $md_pre = 'og' ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'getting all images' ); // Begin timer. $this->p->debug->log_args( array( 'num' => $num, 'size_names' => $size_names, 'mod' => $mod, 'md_pre' => $md_pre, ) ); } $mt_ret = array(); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting all video preview images' ); } $preview_images = $this->get_all_previews( $num, $mod, $md_pre ); if ( empty( $preview_images ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'no video preview images' ); } } else { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'merging video preview images' ); } $mt_ret = array_merge( $mt_ret, $preview_images ); } $num_diff = SucomUtil::array_count_diff( $mt_ret, $num ); $size_names = $this->p->util->get_image_size_names( $size_names ); // Always returns an array. if ( $this->p->debug->enabled ) { $this->p->debug->log_arr( 'size_names', $size_names ); } if ( $num_diff >= 1 ) { // Just in case. $this->p->util->clear_uniq_urls( $size_names ); foreach ( $size_names as $size_name ) { /* * $size_name must be a string. */ $mt_images = $this->get_size_name_images( $num_diff, $size_name, $mod, $md_pre ); if ( empty( $mt_images ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'no images for size name ' . $size_name ); } } else { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'merging ' . count( $mt_images ) . ' images for size name ' . $size_name ); } $mt_ret = array_merge( $mt_ret, $mt_images ); } } } if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'getting all images' ); // End timer. } return $mt_ret; } /* * $size_name must be a string. */ public function get_size_name_images( $num, $size_name, array $mod, $md_pre = 'og' ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } if ( ! is_string( $size_name ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: size_name argument must be a string' ); } return array(); } elseif ( $num < 1 ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: num argument must be 1 or more' ); } return array(); } /* * Example $size_info = Array ( * [size_name] => wpsso-opengraph, * [attachment_id] => false, * [width] => 1200, * [height] => 630, * [crop] => 1, * [is_cropped] => 1, * [dims_transl] => 1200x630 cropped, * [label_transl] => Open Graph (Facebook and oEmbed), * [opt_prefix] => og, * ); */ $size_info = $this->p->util->get_size_info( $size_name ); if ( empty( $size_info[ 'width' ] ) && empty( $size_info[ 'height' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: missing size information for ' . $size_name ); } return array(); } if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting ' . $num . ' images for size name ' . $size_name ); } $mt_ret = array(); if ( is_object( $mod[ 'obj' ] ) && $mod[ 'id' ] ) { // Comment, post, term, or user. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting ' . $mod[ 'name' ] . ' images' ); } if ( $mod[ 'is_post' ] ) { if ( $mod[ 'is_attachment' ] && wp_attachment_is( 'image', $mod[ 'id' ] ) ) { /* * $size_name must be a string. */ $mt_single_image = $this->get_attachment_image( $num, $size_name, $mod[ 'id' ] ); if ( empty( $mt_single_image ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: no attachment image' ); } return $mt_ret; // Stop here. } $mt_ret = array_merge( $mt_ret, $mt_single_image ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'returning attachment images' ); $this->p->debug->log_arr( 'mt_ret', $mt_ret ); } return $mt_ret; // Stop here. } /* * Check for custom meta, featured, or attached image(s). * * Allow for empty post ID in order to execute featured / attached image filters for modules. */ $post_images = $this->get_post_images( $num, $size_name, $mod[ 'id' ], $md_pre ); if ( ! empty( $post_images ) ) { $mt_ret = array_merge( $mt_ret, $post_images ); } } else { /* * get_og_images() provides filter hooks for additional image IDs and URLs. * * Unless $md_pre is 'none', get_og_images() will fallback to using the 'og' custom meta. */ $mt_images = $mod[ 'obj' ]->get_og_images( $num, $size_name, $mod[ 'id' ], $md_pre ); if ( ! empty( $mt_images ) ) { $mt_ret = array_merge( $mt_ret, $mt_images ); } } /* * If we haven't reached the limit of images yet, keep going and check the content text. */ if ( empty( $this->p->options[ 'plugin_content_images' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'skipping content images' ); } } else { if ( ! $this->p->util->is_maxed( $mt_ret, $num ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting content images' ); } $num_diff = SucomUtil::array_count_diff( $mt_ret, $num ); $content_images = $this->get_content_images( $num_diff, $size_name, $mod ); if ( ! empty( $content_images ) ) { $mt_ret = array_merge( $mt_ret, $content_images ); } unset( $content_images ); } } } if ( empty( $mt_ret ) ) { if ( $mod[ 'is_home' ] || $mod[ 'is_archive' ] || $mod[ 'is_post' ] ) { if ( $mod[ 'is_attachment' ] ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'skipping default image: post is attachment' ); } } elseif ( ! $mod[ 'is_public' ] ) { // WooCommerce variations, for example. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'skipping default image: not public' ); $this->p->debug->log_arr( 'mod', $mod ); } } else { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'using the default image' ); } $mt_ret = $this->get_default_images( $size_name ); if ( $this->p->debug->enabled ) { if ( empty( $mt_ret ) ) { $this->p->debug->log( 'no default image' ); } } } } } $this->p->util->slice_max( $mt_ret, $num ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'returning ' . count( $mt_ret ) . ' images' ); $this->p->debug->log_arr( 'mt_ret', $mt_ret ); } return $mt_ret; } /* * $size_name must be a string. */ public function get_post_images( $num, $size_name, $post_id, $md_pre = 'og' ) { if ( $this->p->debug->enabled ) { $this->p->debug->log_args( array( 'num' => $num, 'size_name' => $size_name, 'post_id' => $post_id, 'md_pre' => $md_pre, ) ); } $mt_ret = array(); if ( ! empty( $post_id ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting og images' ); } /* * get_og_images() provides filter hooks for additional image IDs and URLs. * * Unless $md_pre is 'none', get_og_images() will fallback to using the 'og' custom meta. */ $mt_ret = array_merge( $mt_ret, $this->p->post->get_og_images( $num, $size_name, $post_id, $md_pre ) ); } /* * Allow for empty post_id in order to execute featured / attached image filters for modules. */ if ( ! $this->p->util->is_maxed( $mt_ret, $num ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting featured images' ); } $num_diff = SucomUtil::array_count_diff( $mt_ret, $num ); $mt_ret = array_merge( $mt_ret, $this->get_featured( $num_diff, $size_name, $post_id ) ); } /* * Maybe get attached images, including WooCommerce product gallery images. */ if ( empty( $this->p->options[ 'plugin_attached_images' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'skipping attached images' ); } } else { if ( ! $this->p->util->is_maxed( $mt_ret, $num ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting attached images' ); } $num_diff = SucomUtil::array_count_diff( $mt_ret, $num ); $attached_images = $this->get_attached_images( $num_diff, $size_name, $post_id ); if ( ! empty( $attached_images ) ) { $mt_ret = array_merge( $mt_ret, $attached_images ); } unset( $attached_images ); } } if ( $this->p->debug->enabled ) { $this->p->debug->log_arr( 'mt_ret', $mt_ret ); } return $mt_ret; } /* * $size_name must be a string. */ public function get_featured( $num, $size_name, $post_id ) { if ( $this->p->debug->enabled ) { $this->p->debug->log_args( array( 'num' => $num, 'size_name' => $size_name, 'post_id' => $post_id, ) ); } $mt_ret = array(); if ( ! empty( $post_id ) ) { // Just in case. /* * If the post ID is an attachment page, then use the post ID as the image ID. */ if ( ( is_attachment( $post_id ) || 'attachment' === get_post_type( $post_id ) ) && wp_attachment_is( 'image', $post_id ) ) { $pid = $post_id; } elseif ( has_post_thumbnail( $post_id ) ) { $pid = get_post_thumbnail_id( $post_id ); } else $pid = false; $filter_name = 'wpsso_featured_image_id'; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'applying filters "' . $filter_name . '"' ); } $pid = apply_filters( $filter_name, $pid, $post_id ); if ( ! empty( $pid ) && is_numeric( $pid ) ) { // Just in case. /* * Returns an og:image:url value, not an og:image:secure_url. */ $mt_single_image = $this->get_mt_single_image_src( $pid, $size_name ); if ( ! empty( $mt_single_image[ 'og:image:url' ] ) ) { $this->p->util->push_max( $mt_ret, $mt_single_image, $num ); // Continue and apply the 'wpsso_og_featured' filter. } } } $filter_name = 'wpsso_og_featured'; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'applying filters "' . $filter_name . '"' ); } return apply_filters( $filter_name, $mt_ret, $num, $size_name, $post_id ); } /* * $size_name must be a string. * * See WpssoMedia->get_post_images(). */ public function get_attached_images( $num, $size_name, $post_id ) { if ( $this->p->debug->enabled ) { $this->p->debug->log_args( array( 'num' => $num, 'size_name' => $size_name, 'post_id' => $post_id, ) ); } $mt_ret = array(); if ( ! empty( $post_id ) ) { // Just in case. static $local_fifo = array(); if ( ! isset( $local_fifo[ $post_id ] ) ) { /* * Maybe limit the number of array elements. */ $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX ); $local_fifo[ $post_id ] = array(); $images = get_children( array( 'post_parent' => $post_id, 'post_type' => 'attachment', 'post_mime_type' => 'image' // Aka 'image/%' query. ), OBJECT ); // OBJECT, ARRAY_A, or ARRAY_N. /* * Featured images are handled beforehand by WpssoMedia->get_featured(). * * Avoid duplicates by excluding attached image IDs that are also featured image IDs. */ $post_thumbnail_id = get_post_thumbnail_id( $post_id ); foreach ( $images as $attachment ) { if ( ! empty( $attachment->ID ) && $post_thumbnail_id !== $attachment->ID ) { $local_fifo[ $post_id ][] = $attachment->ID; } } rsort( $local_fifo[ $post_id ], SORT_NUMERIC ); $filter_name = 'wpsso_attached_image_ids'; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'applying filters "' . $filter_name . '"' ); } $local_fifo[ $post_id ] = apply_filters( $filter_name, $local_fifo[ $post_id ], $post_id ); $local_fifo[ $post_id ] = array_unique( $local_fifo[ $post_id ] ); // Just in case. } if ( $this->p->debug->enabled ) { $this->p->debug->log( 'found ' . count( $local_fifo[ $post_id ] ) . ' attached images for post id ' . $post_id ); } foreach ( $local_fifo[ $post_id ] as $pid ) { /* * get_mt_single_image_src() returns an og:image:url value, not an og:image:secure_url. */ $mt_single_image = $this->get_mt_single_image_src( $pid, $size_name ); if ( ! empty( $mt_single_image[ 'og:image:url' ] ) ) { if ( $this->p->util->push_max( $mt_ret, $mt_single_image, $num ) ) { break; // Stop here and apply the 'wpsso_og_attached_images' filter. } } } } $filter_name = 'wpsso_og_attached_images'; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'applying filters "' . $filter_name . '"' ); } return apply_filters( $filter_name, $mt_ret, $num, $size_name, $post_id ); } /* * $size_name must be a string. * * See WpssoMedia->get_size_name_images(). */ public function get_content_images( $num = 0, $size_name = 'thumbnail', $mod = true, $content = '' ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } /* * The $mod array argument is preferred but not required. * * $mod = true | false | post_id | $mod array */ if ( ! is_array( $mod ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'optional call to WpssoPage->get_mod()' ); } $mod = $this->p->page->get_mod( $mod ); } $mt_images = array(); /* * Allow custom content to be passed as an argument in $content. */ if ( empty( $content ) ) { $content = $this->p->page->get_the_content( $mod ); $content_passed = false; } else $content_passed = true; if ( empty( $content ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: empty ' . $mod[ 'name' ] . ' content' ); } return $mt_images; } $content_img_preg = $this->default_content_img_preg; $mt_single_image = SucomUtil::get_mt_image_seed(); /* * Allow the html_tag and pid_attr regex to be modified. */ foreach( array( 'html_tag', 'pid_attr' ) as $type ) { $filter_name = 'wpsso_content_image_preg_' . $type; // No need to sanitize. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'applying filters "' . $filter_name . '"' ); } $content_img_preg[ $type ] = apply_filters( $filter_name, $this->default_content_img_preg[ $type ] ); } if ( $this->p->debug->enabled ) { $this->p->debug->log_arr( 'content_img_preg', $content_img_preg ); } /* *' . $func_name . '', $edit_url, $pid, $error_msg ) . ' ' . $regen_msg;
$notice_key = 'wp-get-attachment-metadata-error-for-' . $pid;
$this->p->notice->err( $notice_msg, null, $notice_key );
}
$img_meta = array(); // Avoid "cannot use object of type WP_Error as array" error.
/*
* Image dimensions are missing, but full size image path is present.
*/
} elseif ( isset( $img_meta[ 'file' ] ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'full size image ' . $img_meta[ 'file' ] . ' dimensions missing from image metadata' );
}
if ( $this->p->notice->is_admin_pre_notices() ) {
$notice_msg = sprintf( __( 'Possible %1$s corruption detected - the full size image dimensions for image ID %3$s are missing from the image metadata returned by the WordPress %5$s function.', 'wpsso' ), $img_lib, $edit_url, $pid, $func_url, '' . $func_name . '' ) . ' ' . $regen_msg;
$notice_key = 'full-size-image-' . $pid . '-dimensions-missing';
$this->p->notice->err( $notice_msg, null, $notice_key );
}
/*
* Both the image dimensions and full size image path are missing.
*/
} else {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'full size image file path for ' . $pid . ' missing from image metadata' );
}
if ( $this->p->notice->is_admin_pre_notices() ) {
$notice_msg = sprintf( __( 'Possible %1$s corruption detected - the full size image file path for image ID %3$s is missing from the image metadata returned by the WordPress %5$s function.', 'wpsso' ), $img_lib, $edit_url, $pid, $func_url, '' . $func_name . '' ) . ' ' . $regen_msg;
$notice_key = 'full-size-image-' . $pid . '-file-path-missing';
$this->p->notice->err( $notice_msg, null, $notice_key );
}
}
}
/*
* Only resize our own custom image sizes.
*/
if ( 0 === strpos( $size_name, 'wpsso-' ) ) {
if ( ! $use_full_size ) {
$is_accurate_filename = false;
$is_accurate_width = false;
$is_accurate_height = false;
/*
* Make sure the metadata contains complete image information for the requested size.
*/
if ( ! empty( $img_meta[ 'sizes' ][ $size_name ] ) &&
! empty( $img_meta[ 'sizes' ][ $size_name ][ 'file' ] ) &&
! empty( $img_meta[ 'sizes' ][ $size_name ][ 'width' ] ) &&
! empty( $img_meta[ 'sizes' ][ $size_name ][ 'height' ] ) ) {
/*
* By default, WordPress adds only the resolution of the resized image to the file
* name. If the image size is ours, then add crop information to the file name as
* well. This allows for different cropped versions for the same image resolution.
*/
$new_filepath = $this->maybe_update_cropped_image_filepath( $img_meta[ 'sizes' ][ $size_name ][ 'file' ], $size_info );
$is_accurate_filename = $new_filepath === $img_meta[ 'sizes' ][ $size_name ][ 'file' ] ? true : false;
$is_accurate_width = $size_info[ 'width' ] === $img_meta[ 'sizes' ][ $size_name ][ 'width' ] ? true : false;
$is_accurate_height = $size_info[ 'height' ] === $img_meta[ 'sizes' ][ $size_name ][ 'height' ] ? true : false;
/*
* If not cropped, make sure the resized image respects the original aspect ratio.
*/
if ( ! $size_info[ 'is_cropped' ] ) {
if ( $is_accurate_width && $is_accurate_height &&
isset( $img_meta[ 'width' ] ) && isset( $img_meta[ 'height' ] ) ) {
if ( $img_meta[ 'width' ] > $img_meta[ 'height' ] ) {
$ratio = $img_meta[ 'width' ] / $size_info[ 'width' ];
$check = 'height';
} else {
$ratio = $img_meta[ 'height' ] / $size_info[ 'height' ];
$check = 'width';
}
$should_be = (int) round( $img_meta[ $check ] / $ratio );
/*
* Allow for a +/- one pixel difference.
*/
if ( $img_meta[ 'sizes' ][ $size_name ][ $check ] < ( $should_be - 1 ) ||
$img_meta[ 'sizes' ][ $size_name ][ $check ] > ( $should_be + 1 ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( $size_name . ' image metadata not accurate' );
}
$is_accurate_width = false;
$is_accurate_height = false;
}
}
}
}
/*
* Depending on cropping, one or both sides of the image must be accurate. If the image is
* not accurate, then attempt to create a resized image by calling
* image_make_intermediate_size().
*/
if ( ! $is_accurate_filename ||
( ! $size_info[ 'is_cropped' ] && ( ! $is_accurate_width && ! $is_accurate_height ) ) ||
( $size_info[ 'is_cropped' ] && ( ! $is_accurate_width || ! $is_accurate_height ) ) ) {
if ( $this->can_make_intermediate_size( $img_meta, $size_info ) ) {
// translators: Please ignore - translation uses a different text domain.
$img_lib = __( 'Media Library' );
$func_name = 'image_make_intermediate_size()';
$func_url = __( 'https://developer.wordpress.org/reference/functions/image_make_intermediate_size/', 'wpsso' );
$fullsizepath = get_attached_file( $pid );
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'full size path = ' . $fullsizepath );
$this->p->debug->log( 'calling image_make_intermediate_size()' );
}
/*
* image_make_intermediate_size() resizes an image to make a thumbnail or
* intermediate size. Returns (array|false) metadata array on success,
* false if no image was created.
*/
$mtime_start = microtime( $get_float = true );
$resized_meta = image_make_intermediate_size( $fullsizepath,
$size_info[ 'width' ], $size_info[ 'height' ], $size_info[ 'crop' ] );
$mtime_total = microtime( $get_float = true ) - $mtime_start;
$mtime_max = WPSSO_IMAGE_MAKE_SIZE_MAX_TIME;
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'mtime total = ' . $mtime_total );
if ( false === $resized_meta ) {
$this->p->debug->log( 'resized meta = ' . false );
} else {
$this->p->debug->log_arr( 'resized meta', $resized_meta );
}
}
if ( $mtime_total > $mtime_max ) {
$error_pre = sprintf( __( '%s warning:', 'wpsso' ), __METHOD__ );
$rec_max_msg = sprintf( __( 'longer than recommended max of %1$.3f secs', 'wpsso' ), $mtime_max );
$notice_msg = sprintf( __( 'Slow WordPress function detected - %1$s took %2$.3f secs to make image size "%3$s" from %4$s (%5$s).', 'wpsso' ), '' . $func_name . '', $mtime_total, $size_name, $fullsizepath, $rec_max_msg );
if ( $this->p->debug->enabled ) {
$this->p->debug->log( sprintf( 'slow WordPress function detected - %1$s took %2$.3f secs' .
' to make image size "%3$s" from %4$s', $func_name, $mtime_total, $size_name,
$fullsizepath ) );
}
if ( $this->p->notice->is_admin_pre_notices() ) {
$this->p->notice->err( $notice_msg );
}
SucomUtil::safe_error_log( $error_pre . ' ' . $notice_msg, $strip_html = true );
}
/*
* Returns (array|false) metadata array on success, false if no image was created.
*/
if ( false === $resized_meta ) {
/*
* Add notice only if the admin notices have not already been shown.
*/
if ( $this->p->notice->is_admin_pre_notices() ) {
$notice_msg = sprintf( __( 'Possible %1$s corruption detected - the WordPress %3$s function failed to create the "%4$s" image size (%5$s) from %6$s.', 'wpsso' ), $img_lib, $func_url, '' . $func_name . '', $size_name, $size_info[ 'dims_transl' ], $fullsizepath ) . ' ';
$notice_msg .= sprintf( __( 'You may consider regenerating the sizes of all WordPress Media Library images using one of several available plugins from WordPress.org.', 'wpsso' ), 'https://wordpress.org/plugins/search/regenerate+thumbnails/' );
$notice_key = 'image-make-intermediate-size-' . $fullsizepath . '-failure';
$this->p->notice->err( $notice_msg, null, $notice_key, $dismiss_time = WEEK_IN_SECONDS );
}
$use_full_size = true;
} else {
$img_meta[ 'sizes' ][ $size_name ] = $resized_meta;
wp_update_attachment_metadata( $pid, $img_meta );
}
} else {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'skipped image_make_intermediate_size()' );
}
if ( isset( $img_meta[ 'file' ] ) && isset( $img_meta[ 'width' ] ) && isset( $img_meta[ 'height' ] ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'falling back to full size image ' . $img_meta[ 'file' ] .
' (' . $img_meta[ 'width' ] . 'x' . $img_meta[ 'height' ] . ')' );
}
$use_full_size = true;
}
}
}
}
if ( $use_full_size ) {
if ( isset( $img_meta[ 'sizes' ][ $size_name ] ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'using full size image - removing ' . $size_name . ' image metadata' );
}
unset( $img_meta[ 'sizes' ][ $size_name ] );
wp_update_attachment_metadata( $pid, $img_meta );
}
}
}
/*
* image_downsize() returns an array of image data, or boolean false if no image is available.
*/
$img_downsized = image_downsize( $pid, ( $use_full_size ? 'full' : $size_name ) ); // Returns array or false.
if ( ! is_array( $img_downsized ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'exiting early: image downsize for ' . $size_name . ' returned false' );
}
return self::reset_image_src_args();
}
/*
* Some image_downsize() filter hooks may return only 3 elements, so use array_pad() to sanitize the returned array.
*/
$img_downsized = array_pad( $img_downsized, 4, null );
list(
$img_url,
$img_width,
$img_height,
$img_intermediate
) = apply_filters( 'wpsso_image_downsize', $img_downsized, $pid, $size_name );
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'image downsize for ' . $size_name . ' returned ' . $img_url . ' (' . $img_width . 'x' . $img_height . ')' );
}
if ( empty( $img_url ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'exiting early: image downsize for ' . $size_name . ' returned an empty url' );
}
return self::reset_image_src_args();
}
/*
* Check if image exceeds hard-coded limits (dimensions, ratio, etc.).
*/
$img_within_limits = $this->is_image_within_config_limits( $pid, $size_name, $img_width, $img_height );
if ( apply_filters( 'wpsso_attached_accept_img_dims', $img_within_limits, $img_url, $img_width, $img_height, $size_name, $pid ) ) {
$img_url = $this->p->util->fix_relative_url( $img_url );
$filter_name = 'wpsso_rewrite_image_url';
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'applying filters "' . $filter_name . '" for ' . $img_url );
}
$img_url = apply_filters( 'wpsso_rewrite_image_url', $img_url );
return self::reset_image_src_args( array( $img_url, $img_width, $img_height, $size_info[ 'is_cropped' ], $pid, $img_alt, $size_name ) );
}
return self::reset_image_src_args();
}
/*
* $size_names can be a keyword (ie. 'opengraph' or 'schema'), a registered size name, or an array of size names.
*/
public function get_default_images( $size_names = 'thumbnail' ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
return $this->get_mt_opts_images( $this->p->options, $size_names, $img_prefix = 'og_def_img', $key_num = null, $mt_pre = 'og' );
}
/*
* The returned array can include a varying number of elements, depending on the $media_request value.
*
* $md_pre may be 'none' when getting Open Graph option defaults (and not their custom values).
*
* $size_name should be a string, not an array.
*
* See WpssoCmcfFiltersEdit->filter_mb_sso_edit_media_og_rows() for 'pid'.
* See WpssoGmfFiltersEdit->filter_mb_sso_edit_media_schema_rows() for 'pid'.
* See WpssoEditMedia->filter_mb_sso_edit_media_prio_image_rows() for 'pid'.
* See WpssoEditMedia->filter_mb_sso_edit_media_twitter_rows() for 'pid'.
* See WpssoEditMedia->filter_mb_sso_edit_media_schema_rows() for 'pid'.
* See WpssoEditMedia->filter_mb_sso_edit_media_pinterest_rows() for 'pid'.
* See WpssoProAdminEdit->filter_mb_sso_edit_media_prio_video_rows() for 'og_vid_url', 'og_vid_title',
* 'og_vid_desc', 'og_vid_stream_url', 'og_vid_width', 'og_vid_height', 'og_vid_upload'.
* See WpssoRrssbSubmenuSharePinterest->get_html() for 'og_img_url'.
*/
public function get_media_info( $size_name, array $media_request, array $mod, $md_pre = 'og', $mt_pre = 'og' ) {
if ( ! is_string( $size_name ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'exiting early: $size_name must be a string' );
}
return array();
} elseif ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$media_info = array();
$mt_images = null;
$mt_videos = null;
foreach ( $media_request as $key ) {
switch ( $key ) {
case 'pid':
case ( preg_match( '/^(' . $mt_pre . '_img)/', $key ) ? true : false ):
/*
* Get images only once.
*/
if ( null === $mt_images ) {
$mt_images = $this->get_size_name_images( $num = 1, $size_name, $mod, $md_pre );
}
break;
case ( preg_match( '/^(' . $mt_pre . '_vid)/', $key ) ? true : false ):
/*
* Get videos only once.
*/
if ( null === $mt_videos ) {
$mt_videos = $this->get_all_videos( $num = 1, $mod, $md_pre );
}
break;
}
}
foreach ( $media_request as $key ) {
switch ( $key ) {
case 'pid':
if ( empty( $get_mt_name ) ) {
$get_mt_name = $mt_pre . ':image:id';
}
// No break.
case $mt_pre . '_img_url':
if ( empty( $get_mt_name ) ) {
$get_mt_name = $mt_pre . ':image';
}
// No break.
if ( null !== $mt_videos ) {
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $get_mt_name );
}
if ( empty( $media_info[ $key ] ) ) {
$media_info[ $key ] = $this->get_mt_media_value( $mt_images, $get_mt_name );
}
break;
case $mt_pre . '_img_alt':
$media_info[ $key ] = $this->get_mt_media_value( $mt_images, $mt_pre . ':image:alt' );
break;
case $mt_pre . '_vid_url':
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $mt_pre . ':video' );
break;
case $mt_pre . '_vid_type':
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $mt_pre . ':video:type' );
break;
case $mt_pre . '_vid_title':
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $mt_pre . ':video:title' );
break;
case $mt_pre . '_vid_desc':
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $mt_pre . ':video:description' );
break;
case $mt_pre . '_vid_stream_url':
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $mt_pre . ':video:stream_url' );
break;
case $mt_pre . '_vid_width':
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $mt_pre . ':video:width' );
break;
case $mt_pre . '_vid_height':
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $mt_pre . ':video:height' );
break;
case $mt_pre . '_vid_prev':
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $mt_pre . ':video:thumbnail_url' );
break;
case $mt_pre . '_vid_upload':
$media_info[ $key ] = $this->get_mt_media_value( $mt_videos, $mt_pre . ':video:upload_date' );
WpssoSchema::add_date_time_timezone_opts( $media_info[ $key ], $media_info, $key );
break;
default:
$media_info[ $key ] = '';
break;
}
unset( $get_mt_name );
}
if ( $this->p->debug->enabled ) {
$this->p->debug->log( $media_info );
}
return $media_info;
}
/*
* To get the first image ID or media URL, use the following methods instead:
*
* SucomUtil::get_first_og_image_id()
* SucomUtil::get_first_og_image_url()
* SucomUtil::get_first_mt_media_id()
* SucomUtil::get_first_mt_media_url()
*/
public function get_mt_media_value( $mt_og, $mt_media_pre ) {
if ( empty( $mt_og ) || ! is_array( $mt_og ) ) { // Nothing to do.
return '';
}
if ( ! SucomUtil::is_assoc( $mt_og ) ) {
$og_media = reset( $mt_og ); // Only search the first media array.
}
switch ( $mt_media_pre ) {
/*
* If we're asking for an image or video url, then search all three values sequentially.
*/
case ( preg_match( '/:(image|video)(:secure_url|:url)?$/', $mt_media_pre ) ? true : false ):
$mt_search = array(
$mt_media_pre . ':secure_url', // og:image:secure_url
$mt_media_pre . ':url', // og:image:url
$mt_media_pre, // og:image
);
break;
/*
* Otherwise, only search for that specific meta tag name.
*/
default:
$mt_search = array( $mt_media_pre );
break;
}
foreach ( $mt_search as $key ) {
if ( ! isset( $og_media[ $key ] ) ) {
continue;
} elseif ( '' === $og_media[ $key ] || null === $og_media[ $key ] ) { // Allow for 0.
if ( $this->p->debug->enabled ) {
$this->p->debug->log( $og_media[ $key ] . ' value is empty (skipped)' );
}
} elseif ( WPSSO_UNDEF === $og_media[ $key ] || (string) WPSSO_UNDEF === $og_media[ $key ] ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( $og_media[ $key ] . ' value is ' . WPSSO_UNDEF . ' (skipped)' );
}
} else {
return $og_media[ $key ];
}
}
return '';
}
/*
* $size_names can be a keyword (ie. 'opengraph' or 'schema'), a registered size name, or an array of size names.
*/
public function get_mt_pid_images( $pid, $size_names = 'thumbnail', $mt_pre = 'og' ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log_args( array(
'pid' => $pid,
'size_names' => $size_names,
'mt_pre' => $mt_pre,
) );
}
$mt_ret = array();
$size_names = $this->p->util->get_image_size_names( $size_names ); // Always returns an array.
foreach ( $size_names as $size_name ) {
/*
* get_mt_single_image_src() returns an og:image:url value, not an og:image:secure_url.
*/
$mt_single_image = $this->get_mt_single_image_src( $pid, $size_name, $mt_pre );
if ( ! empty( $mt_single_image[ $mt_pre . ':image:url' ] ) ) {
$mt_ret[] = $mt_single_image;
}
}
return $mt_ret;
}
public function get_mt_single_image_url( $url, $mt_pre = 'og' ) {
$mt_single_image = SucomUtil::get_mt_image_seed( $mt_pre );
$this->add_mt_single_image_url( $mt_single_image, $url, $mt_pre );
return $mt_single_image;
}
/*
* $size_name must be a string.
*/
public function get_mt_single_image_src( $pid, $size_name = 'thumbnail', $mt_pre = 'og' ) {
$mt_single_image = SucomUtil::get_mt_image_seed( $mt_pre );
$this->add_mt_single_image_src( $mt_single_image, $pid, $size_name, $mt_pre );
return $mt_single_image;
}
/*
* $size_name must be a string.
*/
public function add_mt_single_image_src( array &$mt_single_image, $pid, $size_name = 'thumbnail', $mt_pre = 'og' ) {
list(
$mt_single_image[ $mt_pre . ':image:url' ],
$mt_single_image[ $mt_pre . ':image:width' ],
$mt_single_image[ $mt_pre . ':image:height' ],
$mt_single_image[ $mt_pre . ':image:cropped' ],
$mt_single_image[ $mt_pre . ':image:id' ],
$mt_single_image[ $mt_pre . ':image:alt' ],
$mt_single_image[ $mt_pre . ':image:size_name' ],
) = $this->get_attachment_image_src( $pid, $size_name );
}
public function add_mt_single_image_url( array &$mt_single_image, $url, $mt_pre = 'og' ) {
/*
* Example $image_info:
*
* Array (
* [0] => 2048
* [1] => 2048
* [2] => 3
* [3] => width="2048" height="2048"
* [bits] => 8
* [mime] => image/png
* )
*/
list( $img_width, $img_height, $img_type, $img_attr ) = $this->p->util->get_image_url_info( $url );
$mt_single_image[ $mt_pre . ':image:url' ] = $url;
$mt_single_image[ $mt_pre . ':image:width' ] = $img_width;
$mt_single_image[ $mt_pre . ':image:height' ] = $img_height;
}
/*
* $size_names can be a keyword (ie. 'opengraph' or 'schema'), a registered size name, or an array of size names.
*
* $size_name can also be false to ignore image IDs and only use image URLs.
*/
public function get_mt_opts_images( $opts, $size_names, $img_prefix = 'og_img', $key_num = null, $mt_pre = 'og' ) {
$img_opts = array();
foreach ( array( 'id', 'id_lib', 'url', 'url:width', 'url:height' ) as $key ) {
$key_suffix = null === $key_num ? $key : $key . '_' . $key_num; // Use a numbered multi-option key.
$opt_key = $img_prefix . '_' . $key_suffix;
$img_opts[ $key ] = SucomUtilOptions::get_key_value( $opt_key, $opts );
}
if ( $this->p->debug->enabled ) {
$this->p->debug->log_arr( 'img_opts', $img_opts );
}
$mt_ret = array();
if ( ! empty( $img_opts[ 'id' ] ) && ! empty( $size_names ) ) {
$mt_ret = $this->get_mt_pid_images( $img_opts[ 'id' ], $size_names, $mt_pre );
} elseif ( ! empty( $img_opts[ 'url' ] ) ) {
$mt_ret[] = array(
$mt_pre . ':image:url' => $img_opts[ 'url' ],
$mt_pre . ':image:width' => $img_opts[ 'url:width' ] > 0 ? $img_opts[ 'url:width' ] : WPSSO_UNDEF,
$mt_pre . ':image:height' => $img_opts[ 'url:height' ] > 0 ? $img_opts[ 'url:height' ] : WPSSO_UNDEF,
$mt_pre . ':image:cropped' => null,
$mt_pre . ':image:id' => null,
$mt_pre . ':image:alt' => null,
$mt_pre . ':image:size_name' => null,
);
}
return $mt_ret;
}
public function get_mt_img_pre_url( $opts, $img_prefix = 'og_img', $key_num = null, $mt_pre = 'og' ) {
/*
* $size_name is false to ignore image IDs and only use image URLs.
*/
$mt_ret = $this->get_mt_opts_images( $opts, $size_name = false, $img_prefix, $key_num, $mt_pre );
return isset( $mt_ret[ 0 ] ) ? $mt_ret[ 0 ] : array();
}
/*
* Returns an array of single video associative arrays.
*/
public function get_content_videos( $num = 0, $mod = true, $content = '' ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log_args( array(
'num' => $num,
'mod' => $mod,
'content' => strlen( $content ) . ' chars',
) );
}
/*
* The $mod array argument is preferred but not required.
*
* $mod = true | false | post_id | $mod array
*/
if ( ! is_array( $mod ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'optional call to WpssoPage->get_mod()' );
}
$mod = $this->p->page->get_mod( $mod );
}
$mt_videos = array();
/*
* Allow custom content to be passed as an argument in $content.
*/
if ( empty( $content ) ) {
$content = $this->p->page->get_the_content( $mod );
$content_passed = false;
} else $content_passed = true;
if ( empty( $content ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'exiting early: empty ' . $mod[ 'name' ] . ' content' );
}
return $mt_videos;
}
/*
* Detect standard video tags.
*
* $media[ 1 ] = The tag matched (ie. figure, iframe, or embed).
* $media[ 2 ] = The attribute matched.
* $media[ 3 ] = The video URL.
*/
$all_matches = array();
/*
* Matches raw video URLs when the "Use Filtered Content" option is disabled.
*/
if ( preg_match_all( '/<(figure) class="(wp-block-embed[^ "]*) [^"]+">