Permalink
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
WordPress/wp-includes/media.php
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
… lazy-loading it. [52065] originally introduced the logic to guess the LCP image based on certain heuristics and to not lazy-load that image. However, with the introduction of block themes, that logic was not functioning correctly, resulting in all featured images to be lazy-loaded, regardless of whether it was the LCP image or not. Together with an update to the `core/post-featured-image` block included in [55079], this changeset fixes the logic to correctly handle featured images in block themes as well. Additionally, in combination with an update to the `core/template-part` block from [55246], this changeset includes an enhancement which uses the benefits of block template parts to avoid lazy-loading images in the `header` block template part, making the lazy-loading heuristics even more accurate for sites using a block theme. Props flixos90, adamsilverstein, mamaduka, antonvlasenko, shahidul95, reduanmasud, costdev, mukesh27, ironprogrammer, manfcarlo, robinwpdeveloper, spacedmonkey. Fixes #56930. Built from https://develop.svn.wordpress.org/trunk@55318 git-svn-id: http://core.svn.wordpress.org/trunk@54851 1a063a9b-81f0-0310-95a4-ce76da25c4cd
5532 lines (4876 sloc)
188 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* WordPress API for media display. | |
* | |
* @package WordPress | |
* @subpackage Media | |
*/ | |
/** | |
* Retrieves additional image sizes. | |
* | |
* @since 4.7.0 | |
* | |
* @global array $_wp_additional_image_sizes | |
* | |
* @return array Additional images size data. | |
*/ | |
function wp_get_additional_image_sizes() { | |
global $_wp_additional_image_sizes; | |
if ( ! $_wp_additional_image_sizes ) { | |
$_wp_additional_image_sizes = array(); | |
} | |
return $_wp_additional_image_sizes; | |
} | |
/** | |
* Scales down the default size of an image. | |
* | |
* This is so that the image is a better fit for the editor and theme. | |
* | |
* The `$size` parameter accepts either an array or a string. The supported string | |
* values are 'thumb' or 'thumbnail' for the given thumbnail size or defaults at | |
* 128 width and 96 height in pixels. Also supported for the string value is | |
* 'medium', 'medium_large' and 'full'. The 'full' isn't actually supported, but any value other | |
* than the supported will result in the content_width size or 500 if that is | |
* not set. | |
* | |
* Finally, there is a filter named {@see 'editor_max_image_size'}, that will be | |
* called on the calculated array for width and height, respectively. | |
* | |
* @since 2.5.0 | |
* | |
* @global int $content_width | |
* | |
* @param int $width Width of the image in pixels. | |
* @param int $height Height of the image in pixels. | |
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array | |
* of width and height values in pixels (in that order). Default 'medium'. | |
* @param string $context Optional. Could be 'display' (like in a theme) or 'edit' | |
* (like inserting into an editor). Default null. | |
* @return int[] { | |
* An array of width and height values. | |
* | |
* @type int $0 The maximum width in pixels. | |
* @type int $1 The maximum height in pixels. | |
* } | |
*/ | |
function image_constrain_size_for_editor( $width, $height, $size = 'medium', $context = null ) { | |
global $content_width; | |
$_wp_additional_image_sizes = wp_get_additional_image_sizes(); | |
if ( ! $context ) { | |
$context = is_admin() ? 'edit' : 'display'; | |
} | |
if ( is_array( $size ) ) { | |
$max_width = $size[0]; | |
$max_height = $size[1]; | |
} elseif ( 'thumb' === $size || 'thumbnail' === $size ) { | |
$max_width = (int) get_option( 'thumbnail_size_w' ); | |
$max_height = (int) get_option( 'thumbnail_size_h' ); | |
// Last chance thumbnail size defaults. | |
if ( ! $max_width && ! $max_height ) { | |
$max_width = 128; | |
$max_height = 96; | |
} | |
} elseif ( 'medium' === $size ) { | |
$max_width = (int) get_option( 'medium_size_w' ); | |
$max_height = (int) get_option( 'medium_size_h' ); | |
} elseif ( 'medium_large' === $size ) { | |
$max_width = (int) get_option( 'medium_large_size_w' ); | |
$max_height = (int) get_option( 'medium_large_size_h' ); | |
if ( (int) $content_width > 0 ) { | |
$max_width = min( (int) $content_width, $max_width ); | |
} | |
} elseif ( 'large' === $size ) { | |
/* | |
* We're inserting a large size image into the editor. If it's a really | |
* big image we'll scale it down to fit reasonably within the editor | |
* itself, and within the theme's content width if it's known. The user | |
* can resize it in the editor if they wish. | |
*/ | |
$max_width = (int) get_option( 'large_size_w' ); | |
$max_height = (int) get_option( 'large_size_h' ); | |
if ( (int) $content_width > 0 ) { | |
$max_width = min( (int) $content_width, $max_width ); | |
} | |
} elseif ( ! empty( $_wp_additional_image_sizes ) && in_array( $size, array_keys( $_wp_additional_image_sizes ), true ) ) { | |
$max_width = (int) $_wp_additional_image_sizes[ $size ]['width']; | |
$max_height = (int) $_wp_additional_image_sizes[ $size ]['height']; | |
// Only in admin. Assume that theme authors know what they're doing. | |
if ( (int) $content_width > 0 && 'edit' === $context ) { | |
$max_width = min( (int) $content_width, $max_width ); | |
} | |
} else { // $size === 'full' has no constraint. | |
$max_width = $width; | |
$max_height = $height; | |
} | |
/** | |
* Filters the maximum image size dimensions for the editor. | |
* | |
* @since 2.5.0 | |
* | |
* @param int[] $max_image_size { | |
* An array of width and height values. | |
* | |
* @type int $0 The maximum width in pixels. | |
* @type int $1 The maximum height in pixels. | |
* } | |
* @param string|int[] $size Requested image size. Can be any registered image size name, or | |
* an array of width and height values in pixels (in that order). | |
* @param string $context The context the image is being resized for. | |
* Possible values are 'display' (like in a theme) | |
* or 'edit' (like inserting into an editor). | |
*/ | |
list( $max_width, $max_height ) = apply_filters( 'editor_max_image_size', array( $max_width, $max_height ), $size, $context ); | |
return wp_constrain_dimensions( $width, $height, $max_width, $max_height ); | |
} | |
/** | |
* Retrieves width and height attributes using given width and height values. | |
* | |
* Both attributes are required in the sense that both parameters must have a | |
* value, but are optional in that if you set them to false or null, then they | |
* will not be added to the returned string. | |
* | |
* You can set the value using a string, but it will only take numeric values. | |
* If you wish to put 'px' after the numbers, then it will be stripped out of | |
* the return. | |
* | |
* @since 2.5.0 | |
* | |
* @param int|string $width Image width in pixels. | |
* @param int|string $height Image height in pixels. | |
* @return string HTML attributes for width and, or height. | |
*/ | |
function image_hwstring( $width, $height ) { | |
$out = ''; | |
if ( $width ) { | |
$out .= 'width="' . (int) $width . '" '; | |
} | |
if ( $height ) { | |
$out .= 'height="' . (int) $height . '" '; | |
} | |
return $out; | |
} | |
/** | |
* Scales an image to fit a particular size (such as 'thumb' or 'medium'). | |
* | |
* The URL might be the original image, or it might be a resized version. This | |
* function won't create a new resized copy, it will just return an already | |
* resized one if it exists. | |
* | |
* A plugin may use the {@see 'image_downsize'} filter to hook into and offer image | |
* resizing services for images. The hook must return an array with the same | |
* elements that are normally returned from the function. | |
* | |
* @since 2.5.0 | |
* | |
* @param int $id Attachment ID for image. | |
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array | |
* of width and height values in pixels (in that order). Default 'medium'. | |
* @return array|false { | |
* Array of image data, or boolean false if no image is available. | |
* | |
* @type string $0 Image source URL. | |
* @type int $1 Image width in pixels. | |
* @type int $2 Image height in pixels. | |
* @type bool $3 Whether the image is a resized image. | |
* } | |
*/ | |
function image_downsize( $id, $size = 'medium' ) { | |
$is_image = wp_attachment_is_image( $id ); | |
/** | |
* Filters whether to preempt the output of image_downsize(). | |
* | |
* Returning a truthy value from the filter will effectively short-circuit | |
* down-sizing the image, returning that value instead. | |
* | |
* @since 2.5.0 | |
* | |
* @param bool|array $downsize Whether to short-circuit the image downsize. | |
* @param int $id Attachment ID for image. | |
* @param string|int[] $size Requested image size. Can be any registered image size name, or | |
* an array of width and height values in pixels (in that order). | |
*/ | |
$out = apply_filters( 'image_downsize', false, $id, $size ); | |
if ( $out ) { | |
return $out; | |
} | |
$img_url = wp_get_attachment_url( $id ); | |
$meta = wp_get_attachment_metadata( $id ); | |
$width = 0; | |
$height = 0; | |
$is_intermediate = false; | |
$img_url_basename = wp_basename( $img_url ); | |
// If the file isn't an image, attempt to replace its URL with a rendered image from its meta. | |
// Otherwise, a non-image type could be returned. | |
if ( ! $is_image ) { | |
if ( ! empty( $meta['sizes']['full'] ) ) { | |
$img_url = str_replace( $img_url_basename, $meta['sizes']['full']['file'], $img_url ); | |
$img_url_basename = $meta['sizes']['full']['file']; | |
$width = $meta['sizes']['full']['width']; | |
$height = $meta['sizes']['full']['height']; | |
} else { | |
return false; | |
} | |
} | |
// Try for a new style intermediate size. | |
$intermediate = image_get_intermediate_size( $id, $size ); | |
if ( $intermediate ) { | |
$img_url = str_replace( $img_url_basename, $intermediate['file'], $img_url ); | |
$width = $intermediate['width']; | |
$height = $intermediate['height']; | |
$is_intermediate = true; | |
} elseif ( 'thumbnail' === $size && ! empty( $meta['thumb'] ) && is_string( $meta['thumb'] ) ) { | |
// Fall back to the old thumbnail. | |
$imagefile = get_attached_file( $id ); | |
$thumbfile = str_replace( wp_basename( $imagefile ), wp_basename( $meta['thumb'] ), $imagefile ); | |
if ( file_exists( $thumbfile ) ) { | |
$info = wp_getimagesize( $thumbfile ); | |
if ( $info ) { | |
$img_url = str_replace( $img_url_basename, wp_basename( $thumbfile ), $img_url ); | |
$width = $info[0]; | |
$height = $info[1]; | |
$is_intermediate = true; | |
} | |
} | |
} | |
if ( ! $width && ! $height && isset( $meta['width'], $meta['height'] ) ) { | |
// Any other type: use the real image. | |
$width = $meta['width']; | |
$height = $meta['height']; | |
} | |
if ( $img_url ) { | |
// We have the actual image size, but might need to further constrain it if content_width is narrower. | |
list( $width, $height ) = image_constrain_size_for_editor( $width, $height, $size ); | |
return array( $img_url, $width, $height, $is_intermediate ); | |
} | |
return false; | |
} | |
/** | |
* Registers a new image size. | |
* | |
* @since 2.9.0 | |
* | |
* @global array $_wp_additional_image_sizes Associative array of additional image sizes. | |
* | |
* @param string $name Image size identifier. | |
* @param int $width Optional. Image width in pixels. Default 0. | |
* @param int $height Optional. Image height in pixels. Default 0. | |
* @param bool|array $crop Optional. Image cropping behavior. If false, the image will be scaled (default), | |
* If true, image will be cropped to the specified dimensions using center positions. | |
* If an array, the image will be cropped using the array to specify the crop location. | |
* Array values must be in the format: array( x_crop_position, y_crop_position ) where: | |
* - x_crop_position accepts: 'left', 'center', or 'right'. | |
* - y_crop_position accepts: 'top', 'center', or 'bottom'. | |
*/ | |
function add_image_size( $name, $width = 0, $height = 0, $crop = false ) { | |
global $_wp_additional_image_sizes; | |
$_wp_additional_image_sizes[ $name ] = array( | |
'width' => absint( $width ), | |
'height' => absint( $height ), | |
'crop' => $crop, | |
); | |
} | |
/** | |
* Checks if an image size exists. | |
* | |
* @since 3.9.0 | |
* | |
* @param string $name The image size to check. | |
* @return bool True if the image size exists, false if not. | |
*/ | |
function has_image_size( $name ) { | |
$sizes = wp_get_additional_image_sizes(); | |
return isset( $sizes[ $name ] ); | |
} | |
/** | |
* Removes a new image size. | |
* | |
* @since 3.9.0 | |
* | |
* @global array $_wp_additional_image_sizes | |
* | |
* @param string $name The image size to remove. | |
* @return bool True if the image size was successfully removed, false on failure. | |
*/ | |
function remove_image_size( $name ) { | |
global $_wp_additional_image_sizes; | |
if ( isset( $_wp_additional_image_sizes[ $name ] ) ) { | |
unset( $_wp_additional_image_sizes[ $name ] ); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Registers an image size for the post thumbnail. | |
* | |
* @since 2.9.0 | |
* | |
* @see add_image_size() for details on cropping behavior. | |
* | |
* @param int $width Image width in pixels. | |
* @param int $height Image height in pixels. | |
* @param bool|array $crop Optional. Whether to crop images to specified width and height or resize. | |
* An array can specify positioning of the crop area. Default false. | |
*/ | |
function set_post_thumbnail_size( $width = 0, $height = 0, $crop = false ) { | |
add_image_size( 'post-thumbnail', $width, $height, $crop ); | |
} | |
/** | |
* Gets an img tag for an image attachment, scaling it down if requested. | |
* | |
* The {@see 'get_image_tag_class'} filter allows for changing the class name for the | |
* image without having to use regular expressions on the HTML content. The | |
* parameters are: what WordPress will use for the class, the Attachment ID, | |
* image align value, and the size the image should be. | |
* | |
* The second filter, {@see 'get_image_tag'}, has the HTML content, which can then be | |
* further manipulated by a plugin to change all attribute values and even HTML | |
* content. | |
* | |
* @since 2.5.0 | |
* | |
* @param int $id Attachment ID. | |
* @param string $alt Image description for the alt attribute. | |
* @param string $title Image description for the title attribute. | |
* @param string $align Part of the class name for aligning the image. | |
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array of | |
* width and height values in pixels (in that order). Default 'medium'. | |
* @return string HTML IMG element for given image attachment? | |
*/ | |
function get_image_tag( $id, $alt, $title, $align, $size = 'medium' ) { | |
list( $img_src, $width, $height ) = image_downsize( $id, $size ); | |
$hwstring = image_hwstring( $width, $height ); | |
$title = $title ? 'title="' . esc_attr( $title ) . '" ' : ''; | |
$size_class = is_array( $size ) ? implode( 'x', $size ) : $size; | |
$class = 'align' . esc_attr( $align ) . ' size-' . esc_attr( $size_class ) . ' wp-image-' . $id; | |
/** | |
* Filters the value of the attachment's image tag class attribute. | |
* | |
* @since 2.6.0 | |
* | |
* @param string $class CSS class name or space-separated list of classes. | |
* @param int $id Attachment ID. | |
* @param string $align Part of the class name for aligning the image. | |
* @param string|int[] $size Requested image size. Can be any registered image size name, or | |
* an array of width and height values in pixels (in that order). | |
*/ | |
$class = apply_filters( 'get_image_tag_class', $class, $id, $align, $size ); | |
$html = '<img src="' . esc_url( $img_src ) . '" alt="' . esc_attr( $alt ) . '" ' . $title . $hwstring . 'class="' . $class . '" />'; | |
/** | |
* Filters the HTML content for the image tag. | |
* | |
* @since 2.6.0 | |
* | |
* @param string $html HTML content for the image. | |
* @param int $id Attachment ID. | |
* @param string $alt Image description for the alt attribute. | |
* @param string $title Image description for the title attribute. | |
* @param string $align Part of the class name for aligning the image. | |
* @param string|int[] $size Requested image size. Can be any registered image size name, or | |
* an array of width and height values in pixels (in that order). | |
*/ | |
return apply_filters( 'get_image_tag', $html, $id, $alt, $title, $align, $size ); | |
} | |
/** | |
* Calculates the new dimensions for a down-sampled image. | |
* | |
* If either width or height are empty, no constraint is applied on | |
* that dimension. | |
* | |
* @since 2.5.0 | |
* | |
* @param int $current_width Current width of the image. | |
* @param int $current_height Current height of the image. | |
* @param int $max_width Optional. Max width in pixels to constrain to. Default 0. | |
* @param int $max_height Optional. Max height in pixels to constrain to. Default 0. | |
* @return int[] { | |
* An array of width and height values. | |
* | |
* @type int $0 The width in pixels. | |
* @type int $1 The height in pixels. | |
* } | |
*/ | |
function wp_constrain_dimensions( $current_width, $current_height, $max_width = 0, $max_height = 0 ) { | |
if ( ! $max_width && ! $max_height ) { | |
return array( $current_width, $current_height ); | |
} | |
$width_ratio = 1.0; | |
$height_ratio = 1.0; | |
$did_width = false; | |
$did_height = false; | |
if ( $max_width > 0 && $current_width > 0 && $current_width > $max_width ) { | |
$width_ratio = $max_width / $current_width; | |
$did_width = true; | |
} | |
if ( $max_height > 0 && $current_height > 0 && $current_height > $max_height ) { | |
$height_ratio = $max_height / $current_height; | |
$did_height = true; | |
} | |
// Calculate the larger/smaller ratios. | |
$smaller_ratio = min( $width_ratio, $height_ratio ); | |
$larger_ratio = max( $width_ratio, $height_ratio ); | |
if ( (int) round( $current_width * $larger_ratio ) > $max_width || (int) round( $current_height * $larger_ratio ) > $max_height ) { | |
// The larger ratio is too big. It would result in an overflow. | |
$ratio = $smaller_ratio; | |
} else { | |
// The larger ratio fits, and is likely to be a more "snug" fit. | |
$ratio = $larger_ratio; | |
} | |
// Very small dimensions may result in 0, 1 should be the minimum. | |
$w = max( 1, (int) round( $current_width * $ratio ) ); | |
$h = max( 1, (int) round( $current_height * $ratio ) ); | |
/* | |
* Sometimes, due to rounding, we'll end up with a result like this: | |
* 465x700 in a 177x177 box is 117x176... a pixel short. | |
* We also have issues with recursive calls resulting in an ever-changing result. | |
* Constraining to the result of a constraint should yield the original result. | |
* Thus we look for dimensions that are one pixel shy of the max value and bump them up. | |
*/ | |
// Note: $did_width means it is possible $smaller_ratio == $width_ratio. | |
if ( $did_width && $w === $max_width - 1 ) { | |
$w = $max_width; // Round it up. | |
} | |
// Note: $did_height means it is possible $smaller_ratio == $height_ratio. | |
if ( $did_height && $h === $max_height - 1 ) { | |
$h = $max_height; // Round it up. | |
} | |
/** | |
* Filters dimensions to constrain down-sampled images to. | |
* | |
* @since 4.1.0 | |
* | |
* @param int[] $dimensions { | |
* An array of width and height values. | |
* | |
* @type int $0 The width in pixels. | |
* @type int $1 The height in pixels. | |
* } | |
* @param int $current_width The current width of the image. | |
* @param int $current_height The current height of the image. | |
* @param int $max_width The maximum width permitted. | |
* @param int $max_height The maximum height permitted. | |
*/ | |
return apply_filters( 'wp_constrain_dimensions', array( $w, $h ), $current_width, $current_height, $max_width, $max_height ); | |
} | |
/** | |
* Retrieves calculated resize dimensions for use in WP_Image_Editor. | |
* | |
* Calculates dimensions and coordinates for a resized image that fits | |
* within a specified width and height. | |
* | |
* Cropping behavior is dependent on the value of $crop: | |
* 1. If false (default), images will not be cropped. | |
* 2. If an array in the form of array( x_crop_position, y_crop_position ): | |
* - x_crop_position accepts 'left' 'center', or 'right'. | |
* - y_crop_position accepts 'top', 'center', or 'bottom'. | |
* Images will be cropped to the specified dimensions within the defined crop area. | |
* 3. If true, images will be cropped to the specified dimensions using center positions. | |
* | |
* @since 2.5.0 | |
* | |
* @param int $orig_w Original width in pixels. | |
* @param int $orig_h Original height in pixels. | |
* @param int $dest_w New width in pixels. | |
* @param int $dest_h New height in pixels. | |
* @param bool|array $crop Optional. Whether to crop image to specified width and height or resize. | |
* An array can specify positioning of the crop area. Default false. | |
* @return array|false Returned array matches parameters for `imagecopyresampled()`. False on failure. | |
*/ | |
function image_resize_dimensions( $orig_w, $orig_h, $dest_w, $dest_h, $crop = false ) { | |
if ( $orig_w <= 0 || $orig_h <= 0 ) { | |
return false; | |
} | |
// At least one of $dest_w or $dest_h must be specific. | |
if ( $dest_w <= 0 && $dest_h <= 0 ) { | |
return false; | |
} | |
/** | |
* Filters whether to preempt calculating the image resize dimensions. | |
* | |
* Returning a non-null value from the filter will effectively short-circuit | |
* image_resize_dimensions(), returning that value instead. | |
* | |
* @since 3.4.0 | |
* | |
* @param null|mixed $null Whether to preempt output of the resize dimensions. | |
* @param int $orig_w Original width in pixels. | |
* @param int $orig_h Original height in pixels. | |
* @param int $dest_w New width in pixels. | |
* @param int $dest_h New height in pixels. | |
* @param bool|array $crop Whether to crop image to specified width and height or resize. | |
* An array can specify positioning of the crop area. Default false. | |
*/ | |
$output = apply_filters( 'image_resize_dimensions', null, $orig_w, $orig_h, $dest_w, $dest_h, $crop ); | |
if ( null !== $output ) { | |
return $output; | |
} | |
// Stop if the destination size is larger than the original image dimensions. | |
if ( empty( $dest_h ) ) { | |
if ( $orig_w < $dest_w ) { | |
return false; | |
} | |
} elseif ( empty( $dest_w ) ) { | |
if ( $orig_h < $dest_h ) { | |
return false; | |
} | |
} else { | |
if ( $orig_w < $dest_w && $orig_h < $dest_h ) { | |
return false; | |
} | |
} | |
if ( $crop ) { | |
/* | |
* Crop the largest possible portion of the original image that we can size to $dest_w x $dest_h. | |
* Note that the requested crop dimensions are used as a maximum bounding box for the original image. | |
* If the original image's width or height is less than the requested width or height | |
* only the greater one will be cropped. | |
* For example when the original image is 600x300, and the requested crop dimensions are 400x400, | |
* the resulting image will be 400x300. | |
*/ | |
$aspect_ratio = $orig_w / $orig_h; | |
$new_w = min( $dest_w, $orig_w ); | |
$new_h = min( $dest_h, $orig_h ); | |
if ( ! $new_w ) { | |
$new_w = (int) round( $new_h * $aspect_ratio ); | |
} | |
if ( ! $new_h ) { | |
$new_h = (int) round( $new_w / $aspect_ratio ); | |
} | |
$size_ratio = max( $new_w / $orig_w, $new_h / $orig_h ); | |
$crop_w = round( $new_w / $size_ratio ); | |
$crop_h = round( $new_h / $size_ratio ); | |
if ( ! is_array( $crop ) || count( $crop ) !== 2 ) { | |
$crop = array( 'center', 'center' ); | |
} | |
list( $x, $y ) = $crop; | |
if ( 'left' === $x ) { | |
$s_x = 0; | |
} elseif ( 'right' === $x ) { | |
$s_x = $orig_w - $crop_w; | |
} else { | |
$s_x = floor( ( $orig_w - $crop_w ) / 2 ); | |
} | |
if ( 'top' === $y ) { | |
$s_y = 0; | |
} elseif ( 'bottom' === $y ) { | |
$s_y = $orig_h - $crop_h; | |
} else { | |
$s_y = floor( ( $orig_h - $crop_h ) / 2 ); | |
} | |
} else { | |
// Resize using $dest_w x $dest_h as a maximum bounding box. | |
$crop_w = $orig_w; | |
$crop_h = $orig_h; | |
$s_x = 0; | |
$s_y = 0; | |
list( $new_w, $new_h ) = wp_constrain_dimensions( $orig_w, $orig_h, $dest_w, $dest_h ); | |
} | |
if ( wp_fuzzy_number_match( $new_w, $orig_w ) && wp_fuzzy_number_match( $new_h, $orig_h ) ) { | |
// The new size has virtually the same dimensions as the original image. | |
/** | |
* Filters whether to proceed with making an image sub-size with identical dimensions | |
* with the original/source image. Differences of 1px may be due to rounding and are ignored. | |
* | |
* @since 5.3.0 | |
* | |
* @param bool $proceed The filtered value. | |
* @param int $orig_w Original image width. | |
* @param int $orig_h Original image height. | |
*/ | |
$proceed = (bool) apply_filters( 'wp_image_resize_identical_dimensions', false, $orig_w, $orig_h ); | |
if ( ! $proceed ) { | |
return false; | |
} | |
} | |
// The return array matches the parameters to imagecopyresampled(). | |
// int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h | |
return array( 0, 0, (int) $s_x, (int) $s_y, (int) $new_w, (int) $new_h, (int) $crop_w, (int) $crop_h ); | |
} | |
/** | |
* Resizes an image to make a thumbnail or intermediate size. | |
* | |
* The returned array has the file size, the image width, and image height. The | |
* {@see 'image_make_intermediate_size'} filter can be used to hook in and change the | |
* values of the returned array. The only parameter is the resized file path. | |
* | |
* @since 2.5.0 | |
* | |
* @param string $file File path. | |
* @param int $width Image width. | |
* @param int $height Image height. | |
* @param bool $crop Optional. Whether to crop image to specified width and height or resize. | |
* Default false. | |
* @return array|false Metadata array on success. False if no image was created. | |
*/ | |
function image_make_intermediate_size( $file, $width, $height, $crop = false ) { | |
if ( $width || $height ) { | |
$editor = wp_get_image_editor( $file ); | |
if ( is_wp_error( $editor ) || is_wp_error( $editor->resize( $width, $height, $crop ) ) ) { | |
return false; | |
} | |
$resized_file = $editor->save(); | |
if ( ! is_wp_error( $resized_file ) && $resized_file ) { | |
unset( $resized_file['path'] ); | |
return $resized_file; | |
} | |
} | |
return false; | |
} | |
/** | |
* Helper function to test if aspect ratios for two images match. | |
* | |
* @since 4.6.0 | |
* | |
* @param int $source_width Width of the first image in pixels. | |
* @param int $source_height Height of the first image in pixels. | |
* @param int $target_width Width of the second image in pixels. | |
* @param int $target_height Height of the second image in pixels. | |
* @return bool True if aspect ratios match within 1px. False if not. | |
*/ | |
function wp_image_matches_ratio( $source_width, $source_height, $target_width, $target_height ) { | |
/* | |
* To test for varying crops, we constrain the dimensions of the larger image | |
* to the dimensions of the smaller image and see if they match. | |
*/ | |
if ( $source_width > $target_width ) { | |
$constrained_size = wp_constrain_dimensions( $source_width, $source_height, $target_width ); | |
$expected_size = array( $target_width, $target_height ); | |
} else { | |
$constrained_size = wp_constrain_dimensions( $target_width, $target_height, $source_width ); | |
$expected_size = array( $source_width, $source_height ); | |
} | |
// If the image dimensions are within 1px of the expected size, we consider it a match. | |
$matched = ( wp_fuzzy_number_match( $constrained_size[0], $expected_size[0] ) && wp_fuzzy_number_match( $constrained_size[1], $expected_size[1] ) ); | |
return $matched; | |
} | |
/** | |
* Retrieves the image's intermediate size (resized) path, width, and height. | |
* | |
* The $size parameter can be an array with the width and height respectively. | |
* If the size matches the 'sizes' metadata array for width and height, then it | |
* will be used. If there is no direct match, then the nearest image size larger | |
* than the specified size will be used. If nothing is found, then the function | |
* will break out and return false. | |
* | |
* The metadata 'sizes' is used for compatible sizes that can be used for the | |
* parameter $size value. | |
* | |
* The url path will be given, when the $size parameter is a string. | |
* | |
* If you are passing an array for the $size, you should consider using | |
* add_image_size() so that a cropped version is generated. It's much more | |
* efficient than having to find the closest-sized image and then having the | |
* browser scale down the image. | |
* | |
* @since 2.5.0 | |
* | |
* @param int $post_id Attachment ID. | |
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array | |
* of width and height values in pixels (in that order). Default 'thumbnail'. | |
* @return array|false { | |
* Array of file relative path, width, and height on success. Additionally includes absolute | |
* path and URL if registered size is passed to `$size` parameter. False on failure. | |
* | |
* @type string $file Path of image relative to uploads directory. | |
* @type int $width Width of image in pixels. | |
* @type int $height Height of image in pixels. | |
* @type string $path Absolute filesystem path of image. | |
* @type string $url URL of image. | |
* } | |
*/ | |
function image_get_intermediate_size( $post_id, $size = 'thumbnail' ) { | |
$imagedata = wp_get_attachment_metadata( $post_id ); | |
if ( ! $size || ! is_array( $imagedata ) || empty( $imagedata['sizes'] ) ) { | |
return false; | |
} | |
$data = array(); | |
// Find the best match when '$size' is an array. | |
if ( is_array( $size ) ) { | |
$candidates = array(); | |
if ( ! isset( $imagedata['file'] ) && isset( $imagedata['sizes']['full'] ) ) { | |
$imagedata['height'] = $imagedata['sizes']['full']['height']; | |
$imagedata['width'] = $imagedata['sizes']['full']['width']; | |
} | |
foreach ( $imagedata['sizes'] as $_size => $data ) { | |
// If there's an exact match to an existing image size, short circuit. | |
if ( (int) $data['width'] === (int) $size[0] && (int) $data['height'] === (int) $size[1] ) { | |
$candidates[ $data['width'] * $data['height'] ] = $data; | |
break; | |
} | |
// If it's not an exact match, consider larger sizes with the same aspect ratio. | |
if ( $data['width'] >= $size[0] && $data['height'] >= $size[1] ) { | |
// If '0' is passed to either size, we test ratios against the original file. | |
if ( 0 === $size[0] || 0 === $size[1] ) { | |
$same_ratio = wp_image_matches_ratio( $data['width'], $data['height'], $imagedata['width'], $imagedata['height'] ); | |
} else { | |
$same_ratio = wp_image_matches_ratio( $data['width'], $data['height'], $size[0], $size[1] ); | |
} | |
if ( $same_ratio ) { | |
$candidates[ $data['width'] * $data['height'] ] = $data; | |
} | |
} | |
} | |
if ( ! empty( $candidates ) ) { | |
// Sort the array by size if we have more than one candidate. | |
if ( 1 < count( $candidates ) ) { | |
ksort( $candidates ); | |
} | |
$data = array_shift( $candidates ); | |
/* | |
* When the size requested is smaller than the thumbnail dimensions, we | |
* fall back to the thumbnail size to maintain backward compatibility with | |
* pre 4.6 versions of WordPress. | |
*/ | |
} elseif ( ! empty( $imagedata['sizes']['thumbnail'] ) && $imagedata['sizes']['thumbnail']['width'] >= $size[0] && $imagedata['sizes']['thumbnail']['width'] >= $size[1] ) { | |
$data = $imagedata['sizes']['thumbnail']; | |
} else { | |
return false; | |
} | |
// Constrain the width and height attributes to the requested values. | |
list( $data['width'], $data['height'] ) = image_constrain_size_for_editor( $data['width'], $data['height'], $size ); | |
} elseif ( ! empty( $imagedata['sizes'][ $size ] ) ) { | |
$data = $imagedata['sizes'][ $size ]; | |
} | |
// If we still don't have a match at this point, return false. | |
if ( empty( $data ) ) { | |
return false; | |
} | |
// Include the full filesystem path of the intermediate file. | |
if ( empty( $data['path'] ) && ! empty( $data['file'] ) && ! empty( $imagedata['file'] ) ) { | |
$file_url = wp_get_attachment_url( $post_id ); | |
$data['path'] = path_join( dirname( $imagedata['file'] ), $data['file'] ); | |
$data['url'] = path_join( dirname( $file_url ), $data['file'] ); | |
} | |
/** | |
* Filters the output of image_get_intermediate_size() | |
* | |
* @since 4.4.0 | |
* | |
* @see image_get_intermediate_size() | |
* | |
* @param array $data Array of file relative path, width, and height on success. May also include | |
* file absolute path and URL. | |
* @param int $post_id The ID of the image attachment. | |
* @param string|int[] $size Requested image size. Can be any registered image size name, or | |
* an array of width and height values in pixels (in that order). | |
*/ | |
return apply_filters( 'image_get_intermediate_size', $data, $post_id, $size ); | |
} | |
/** | |
* Gets the available intermediate image size names. | |
* | |
* @since 3.0.0 | |
* | |
* @return string[] An array of image size names. | |
*/ | |
function get_intermediate_image_sizes() { | |
$default_sizes = array( 'thumbnail', 'medium', 'medium_large', 'large' ); | |
$additional_sizes = wp_get_additional_image_sizes(); | |
if ( ! empty( $additional_sizes ) ) { | |
$default_sizes = array_merge( $default_sizes, array_keys( $additional_sizes ) ); | |
} | |
/** | |
* Filters the list of intermediate image sizes. | |
* | |
* @since 2.5.0 | |
* | |
* @param string[] $default_sizes An array of intermediate image size names. Defaults | |
* are 'thumbnail', 'medium', 'medium_large', 'large'. | |
*/ | |
return apply_filters( 'intermediate_image_sizes', $default_sizes ); | |
} | |
/** | |
* Returns a normalized list of all currently registered image sub-sizes. | |
* | |
* @since 5.3.0 | |
* @uses wp_get_additional_image_sizes() | |
* @uses get_intermediate_image_sizes() | |
* | |
* @return array[] Associative array of arrays of image sub-size information, | |
* keyed by image size name. | |
*/ | |
function wp_get_registered_image_subsizes() { | |
$additional_sizes = wp_get_additional_image_sizes(); | |
$all_sizes = array(); | |
foreach ( get_intermediate_image_sizes() as $size_name ) { | |
$size_data = array( | |
'width' => 0, | |
'height' => 0, | |
'crop' => false, | |
); | |
if ( isset( $additional_sizes[ $size_name ]['width'] ) ) { | |
// For sizes added by plugins and themes. | |
$size_data['width'] = (int) $additional_sizes[ $size_name ]['width']; | |
} else { | |
// For default sizes set in options. | |
$size_data['width'] = (int) get_option( "{$size_name}_size_w" ); | |
} | |
if ( isset( $additional_sizes[ $size_name ]['height'] ) ) { | |
$size_data['height'] = (int) $additional_sizes[ $size_name ]['height']; | |
} else { | |
$size_data['height'] = (int) get_option( "{$size_name}_size_h" ); | |
} | |
if ( empty( $size_data['width'] ) && empty( $size_data['height'] ) ) { | |
// This size isn't set. | |
continue; | |
} | |
if ( isset( $additional_sizes[ $size_name ]['crop'] ) ) { | |
$size_data['crop'] = $additional_sizes[ $size_name ]['crop']; | |
} else { | |
$size_data['crop'] = get_option( "{$size_name}_crop" ); | |
} | |
if ( ! is_array( $size_data['crop'] ) || empty( $size_data['crop'] ) ) { | |
$size_data['crop'] = (bool) $size_data['crop']; | |
} | |
$all_sizes[ $size_name ] = $size_data; | |
} | |
return $all_sizes; | |
} | |
/** | |
* Retrieves an image to represent an attachment. | |
* | |
* @since 2.5.0 | |
* | |
* @param int $attachment_id Image attachment ID. | |
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array of | |
* width and height values in pixels (in that order). Default 'thumbnail'. | |
* @param bool $icon Optional. Whether the image should fall back to a mime type icon. Default false. | |
* @return array|false { | |
* Array of image data, or boolean false if no image is available. | |
* | |
* @type string $0 Image source URL. | |
* @type int $1 Image width in pixels. | |
* @type int $2 Image height in pixels. | |
* @type bool $3 Whether the image is a resized image. | |
* } | |
*/ | |
function wp_get_attachment_image_src( $attachment_id, $size = 'thumbnail', $icon = false ) { | |
// Get a thumbnail or intermediate image if there is one. | |
$image = image_downsize( $attachment_id, $size ); | |
if ( ! $image ) { | |
$src = false; | |
if ( $icon ) { | |
$src = wp_mime_type_icon( $attachment_id ); | |
if ( $src ) { | |
/** This filter is documented in wp-includes/post.php */ | |
$icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/media' ); | |
$src_file = $icon_dir . '/' . wp_basename( $src ); | |
list( $width, $height ) = wp_getimagesize( $src_file ); | |
} | |
} | |
if ( $src && $width && $height ) { | |
$image = array( $src, $width, $height, false ); | |
} | |
} | |
/** | |
* Filters the attachment image source result. | |
* | |
* @since 4.3.0 | |
* | |
* @param array|false $image { | |
* Array of image data, or boolean false if no image is available. | |
* | |
* @type string $0 Image source URL. | |
* @type int $1 Image width in pixels. | |
* @type int $2 Image height in pixels. | |
* @type bool $3 Whether the image is a resized image. | |
* } | |
* @param int $attachment_id Image attachment ID. | |
* @param string|int[] $size Requested image size. Can be any registered image size name, or | |
* an array of width and height values in pixels (in that order). | |
* @param bool $icon Whether the image should be treated as an icon. | |
*/ | |
return apply_filters( 'wp_get_attachment_image_src', $image, $attachment_id, $size, $icon ); | |
} | |
/** | |
* Gets an HTML img element representing an image attachment. | |
* | |
* While `$size` will accept an array, it is better to register a size with | |
* add_image_size() so that a cropped version is generated. It's much more | |
* efficient than having to find the closest-sized image and then having the | |
* browser scale down the image. | |
* | |
* @since 2.5.0 | |
* @since 4.4.0 The `$srcset` and `$sizes` attributes were added. | |
* @since 5.5.0 The `$loading` attribute was added. | |
* @since 6.1.0 The `$decoding` attribute was added. | |
* | |
* @param int $attachment_id Image attachment ID. | |
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array | |
* of width and height values in pixels (in that order). Default 'thumbnail'. | |
* @param bool $icon Optional. Whether the image should be treated as an icon. Default false. | |
* @param string|array $attr { | |
* Optional. Attributes for the image markup. | |
* | |
* @type string $src Image attachment URL. | |
* @type string $class CSS class name or space-separated list of classes. | |
* Default `attachment-$size_class size-$size_class`, | |
* where `$size_class` is the image size being requested. | |
* @type string $alt Image description for the alt attribute. | |
* @type string $srcset The 'srcset' attribute value. | |
* @type string $sizes The 'sizes' attribute value. | |
* @type string|false $loading The 'loading' attribute value. Passing a value of false | |
* will result in the attribute being omitted for the image. | |
* Defaults to 'lazy', depending on wp_lazy_loading_enabled(). | |
* @type string $decoding The 'decoding' attribute value. Possible values are | |
* 'async' (default), 'sync', or 'auto'. Passing false or an empty | |
* string will result in the attribute being omitted. | |
* } | |
* @return string HTML img element or empty string on failure. | |
*/ | |
function wp_get_attachment_image( $attachment_id, $size = 'thumbnail', $icon = false, $attr = '' ) { | |
$html = ''; | |
$image = wp_get_attachment_image_src( $attachment_id, $size, $icon ); | |
if ( $image ) { | |
list( $src, $width, $height ) = $image; | |
$attachment = get_post( $attachment_id ); | |
$hwstring = image_hwstring( $width, $height ); | |
$size_class = $size; | |
if ( is_array( $size_class ) ) { | |
$size_class = implode( 'x', $size_class ); | |
} | |
$default_attr = array( | |
'src' => $src, | |
'class' => "attachment-$size_class size-$size_class", | |
'alt' => trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ), | |
'decoding' => 'async', | |
); | |
// Add `loading` attribute. | |
if ( wp_lazy_loading_enabled( 'img', 'wp_get_attachment_image' ) ) { | |
$default_attr['loading'] = wp_get_loading_attr_default( 'wp_get_attachment_image' ); | |
} | |
$attr = wp_parse_args( $attr, $default_attr ); | |
// Omit the `decoding` attribute if the value is invalid according to the spec. | |
if ( empty( $attr['decoding'] ) || ! in_array( $attr['decoding'], array( 'async', 'sync', 'auto' ), true ) ) { | |
unset( $attr['decoding'] ); | |
} | |
// If the default value of `lazy` for the `loading` attribute is overridden | |
// to omit the attribute for this image, ensure it is not included. | |
if ( array_key_exists( 'loading', $attr ) && ! $attr['loading'] ) { | |
unset( $attr['loading'] ); | |
} | |
// Generate 'srcset' and 'sizes' if not already present. | |
if ( empty( $attr['srcset'] ) ) { | |
$image_meta = wp_get_attachment_metadata( $attachment_id ); | |
if ( is_array( $image_meta ) ) { | |
$size_array = array( absint( $width ), absint( $height ) ); | |
$srcset = wp_calculate_image_srcset( $size_array, $src, $image_meta, $attachment_id ); | |
$sizes = wp_calculate_image_sizes( $size_array, $src, $image_meta, $attachment_id ); | |
if ( $srcset && ( $sizes || ! empty( $attr['sizes'] ) ) ) { | |
$attr['srcset'] = $srcset; | |
if ( empty( $attr['sizes'] ) ) { | |
$attr['sizes'] = $sizes; | |
} | |
} | |
} | |
} | |
/** | |
* Filters the list of attachment image attributes. | |
* | |
* @since 2.8.0 | |
* | |
* @param string[] $attr Array of attribute values for the image markup, keyed by attribute name. | |
* See wp_get_attachment_image(). | |
* @param WP_Post $attachment Image attachment post. | |
* @param string|int[] $size Requested image size. Can be any registered image size name, or | |
* an array of width and height values in pixels (in that order). | |
*/ | |
$attr = apply_filters( 'wp_get_attachment_image_attributes', $attr, $attachment, $size ); | |
$attr = array_map( 'esc_attr', $attr ); | |
$html = rtrim( "<img $hwstring" ); | |
foreach ( $attr as $name => $value ) { | |
$html .= " $name=" . '"' . $value . '"'; | |
} | |
$html .= ' />'; | |
} | |
/** | |
* Filters the HTML img element representing an image attachment. | |
* | |
* @since 5.6.0 | |
* | |
* @param string $html HTML img element or empty string on failure. | |
* @param int $attachment_id Image attachment ID. | |
* @param string|int[] $size Requested image size. Can be any registered image size name, or | |
* an array of width and height values in pixels (in that order). | |
* @param bool $icon Whether the image should be treated as an icon. | |
* @param string[] $attr Array of attribute values for the image markup, keyed by attribute name. | |
* See wp_get_attachment_image(). | |
*/ | |
return apply_filters( 'wp_get_attachment_image', $html, $attachment_id, $size, $icon, $attr ); | |
} | |
/** | |
* Gets the URL of an image attachment. | |
* | |
* @since 4.4.0 | |
* | |
* @param int $attachment_id Image attachment ID. | |
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array of | |
* width and height values in pixels (in that order). Default 'thumbnail'. | |
* @param bool $icon Optional. Whether the image should be treated as an icon. Default false. | |
* @return string|false Attachment URL or false if no image is available. If `$size` does not match | |
* any registered image size, the original image URL will be returned. | |
*/ | |
function wp_get_attachment_image_url( $attachment_id, $size = 'thumbnail', $icon = false ) { | |
$image = wp_get_attachment_image_src( $attachment_id, $size, $icon ); | |
return isset( $image[0] ) ? $image[0] : false; | |
} | |
/** | |
* Gets the attachment path relative to the upload directory. | |
* | |
* @since 4.4.1 | |
* @access private | |
* | |
* @param string $file Attachment file name. | |
* @return string Attachment path relative to the upload directory. | |
*/ | |
function _wp_get_attachment_relative_path( $file ) { | |
$dirname = dirname( $file ); | |
if ( '.' === $dirname ) { | |
return ''; | |
} | |
if ( false !== strpos( $dirname, 'wp-content/uploads' ) ) { | |
// Get the directory name relative to the upload directory (back compat for pre-2.7 uploads). | |
$dirname = substr( $dirname, strpos( $dirname, 'wp-content/uploads' ) + 18 ); | |
$dirname = ltrim( $dirname, '/' ); | |
} | |
return $dirname; | |
} | |
/** | |
* Gets the image size as array from its meta data. | |
* | |
* Used for responsive images. | |
* | |
* @since 4.4.0 | |
* @access private | |
* | |
* @param string $size_name Image size. Accepts any registered image size name. | |
* @param array $image_meta The image meta data. | |
* @return array|false { | |
* Array of width and height or false if the size isn't present in the meta data. | |
* | |
* @type int $0 Image width. | |
* @type int $1 Image height. | |
* } | |
*/ | |
function _wp_get_image_size_from_meta( $size_name, $image_meta ) { | |
if ( 'full' === $size_name ) { | |
return array( | |
absint( $image_meta['width'] ), | |
absint( $image_meta['height'] ), | |
); | |
} elseif ( ! empty( $image_meta['sizes'][ $size_name ] ) ) { | |
return array( | |
absint( $image_meta['sizes'][ $size_name ]['width'] ), | |
absint( $image_meta['sizes'][ $size_name ]['height'] ), | |
); | |
} | |
return false; | |
} | |
/** | |
* Retrieves the value for an image attachment's 'srcset' attribute. | |
* | |
* @since 4.4.0 | |
* | |
* @see wp_calculate_image_srcset() | |
* | |
* @param int $attachment_id Image attachment ID. | |
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array of | |
* width and height values in pixels (in that order). Default 'medium'. | |
* @param array $image_meta Optional. The image meta data as returned by 'wp_get_attachment_metadata()'. | |
* Default null. | |
* @return string|false A 'srcset' value string or false. | |
*/ | |
function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $image_meta = null ) { | |
$image = wp_get_attachment_image_src( $attachment_id, $size ); | |
if ( ! $image ) { | |
return false; | |
} | |
if ( ! is_array( $image_meta ) ) { | |
$image_meta = wp_get_attachment_metadata( $attachment_id ); | |
} | |
$image_src = $image[0]; | |
$size_array = array( | |
absint( $image[1] ), | |
absint( $image[2] ), | |
); | |
return wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id ); | |
} | |
/** | |
* A helper function to calculate the image sources to include in a 'srcset' attribute. | |
* | |
* @since 4.4.0 | |
* | |
* @param int[] $size_array { | |
* An array of width and height values. | |
* | |
* @type int $0 The width in pixels. | |
* @type int $1 The height in pixels. | |
* } | |
* @param string $image_src The 'src' of the image. | |
* @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. | |
* @param int $attachment_id Optional. The image attachment ID. Default 0. | |
* @return string|false The 'srcset' attribute value. False on error or when only one source exists. | |
*/ | |
function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id = 0 ) { | |
/** | |
* Pre-filters the image meta to be able to fix inconsistencies in the stored data. | |
* | |
* @since 4.5.0 | |
* | |
* @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. | |
* @param int[] $size_array { | |
* An array of requested width and height values. | |
* | |
* @type int $0 The width in pixels. | |
* @type int $1 The height in pixels. | |
* } | |
* @param string $image_src The 'src' of the image. | |
* @param int $attachment_id The image attachment ID or 0 if not supplied. | |
*/ | |
$image_meta = apply_filters( 'wp_calculate_image_srcset_meta', $image_meta, $size_array, $image_src, $attachment_id ); | |
if ( empty( $image_meta['sizes'] ) || ! isset( $image_meta['file'] ) || strlen( $image_meta['file'] ) < 4 ) { | |
return false; | |
} | |
$image_sizes = $image_meta['sizes']; | |
// Get the width and height of the image. | |
$image_width = (int) $size_array[0]; | |
$image_height = (int) $size_array[1]; | |
// Bail early if error/no width. | |
if ( $image_width < 1 ) { | |
return false; | |
} | |
$image_basename = wp_basename( $image_meta['file'] ); | |
/* | |
* WordPress flattens animated GIFs into one frame when generating intermediate sizes. | |
* To avoid hiding animation in user content, if src is a full size GIF, a srcset attribute is not generated. | |
* If src is an intermediate size GIF, the full size is excluded from srcset to keep a flattened GIF from becoming animated. | |
*/ | |
if ( ! isset( $image_sizes['thumbnail']['mime-type'] ) || 'image/gif' !== $image_sizes['thumbnail']['mime-type'] ) { | |
$image_sizes[] = array( | |
'width' => $image_meta['width'], | |
'height' => $image_meta['height'], | |
'file' => $image_basename, | |
); | |
} elseif ( strpos( $image_src, $image_meta['file'] ) ) { | |
return false; | |
} | |
// Retrieve the uploads sub-directory from the full size image. | |
$dirname = _wp_get_attachment_relative_path( $image_meta['file'] ); | |
if ( $dirname ) { | |
$dirname = trailingslashit( $dirname ); | |
} | |
$upload_dir = wp_get_upload_dir(); | |
$image_baseurl = trailingslashit( $upload_dir['baseurl'] ) . $dirname; | |
/* | |
* If currently on HTTPS, prefer HTTPS URLs when we know they're supported by the domain | |
* (which is to say, when they share the domain name of the current request). | |
*/ | |
if ( is_ssl() && 'https' !== substr( $image_baseurl, 0, 5 ) && parse_url( $image_baseurl, PHP_URL_HOST ) === $_SERVER['HTTP_HOST'] ) { | |
$image_baseurl = set_url_scheme( $image_baseurl, 'https' ); | |
} | |
/* | |
* Images that have been edited in WordPress after being uploaded will | |
* contain a unique hash. Look for that hash and use it later to filter | |
* out images that are leftovers from previous versions. | |
*/ | |
$image_edited = preg_match( '/-e[0-9]{13}/', wp_basename( $image_src ), $image_edit_hash ); | |
/** | |
* Filters the maximum image width to be included in a 'srcset' attribute. | |
* | |
* @since 4.4.0 | |
* | |
* @param int $max_width The maximum image width to be included in the 'srcset'. Default '2048'. | |
* @param int[] $size_array { | |
* An array of requested width and height values. | |
* | |
* @type int $0 The width in pixels. | |
* @type int $1 The height in pixels. | |
* } | |
*/ | |
$max_srcset_image_width = apply_filters( 'max_srcset_image_width', 2048, $size_array ); | |
// Array to hold URL candidates. | |
$sources = array(); | |
/** | |
* To make sure the ID matches our image src, we will check to see if any sizes in our attachment | |
* meta match our $image_src. If no matches are found we don't return a srcset to avoid serving | |
* an incorrect image. See #35045. | |
*/ | |
$src_matched = false; | |
/* | |
* Loop through available images. Only use images that are resized | |
* versions of the same edit. | |
*/ | |
foreach ( $image_sizes as $image ) { | |
$is_src = false; | |
// Check if image meta isn't corrupted. | |
if ( ! is_array( $image ) ) { | |
continue; | |
} | |
// If the file name is part of the `src`, we've confirmed a match. | |
if ( ! $src_matched && false !== strpos( $image_src, $dirname . $image['file'] ) ) { | |
$src_matched = true; | |
$is_src = true; | |
} | |
// Filter out images that are from previous edits. | |
if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) { | |
continue; | |
} | |
/* | |
* Filters out images that are wider than '$max_srcset_image_width' unless | |
* that file is in the 'src' attribute. | |
*/ | |
if ( $max_srcset_image_width && $image['width'] > $max_srcset_image_width && ! $is_src ) { | |
continue; | |
} | |
// If the image dimensions are within 1px of the expected size, use it. | |
if ( wp_image_matches_ratio( $image_width, $image_height, $image['width'], $image['height'] ) ) { | |
// Add the URL, descriptor, and value to the sources array to be returned. | |
$source = array( | |
'url' => $image_baseurl . $image['file'], | |
'descriptor' => 'w', | |
'value' => $image['width'], | |
); | |
// The 'src' image has to be the first in the 'srcset', because of a bug in iOS8. See #35030. | |
if ( $is_src ) { | |
$sources = array( $image['width'] => $source ) + $sources; | |
} else { | |
$sources[ $image['width'] ] = $source; | |
} | |
} | |
} | |
/** | |
* Filters an image's 'srcset' sources. | |
* | |
* @since 4.4.0 | |
* | |
* @param array $sources { | |
* One or more arrays of source data to include in the 'srcset'. | |
* | |
* @type array $width { | |
* @type string $url The URL of an image source. | |
* @type string $descriptor The descriptor type used in the image candidate string, | |
* either 'w' or 'x'. | |
* @type int $value The source width if paired with a 'w' descriptor, or a | |
* pixel density value if paired with an 'x' descriptor. | |
* } | |
* } | |
* @param array $size_array { | |
* An array of requested width and height values. | |
* | |
* @type int $0 The width in pixels. | |
* @type int $1 The height in pixels. | |
* } | |
* @param string $image_src The 'src' of the image. | |
* @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. | |
* @param int $attachment_id Image attachment ID or 0. | |
*/ | |
$sources = apply_filters( 'wp_calculate_image_srcset', $sources, $size_array, $image_src, $image_meta, $attachment_id ); | |
// Only return a 'srcset' value if there is more than one source. | |
if ( ! $src_matched || ! is_array( $sources ) || count( $sources ) < 2 ) { | |
return false; | |
} | |
$srcset = ''; | |
foreach ( $sources as $source ) { | |
$srcset .= str_replace( ' ', '%20', $source['url'] ) . ' ' . $source['value'] . $source['descriptor'] . ', '; | |
} | |
return rtrim( $srcset, ', ' ); | |
} | |
/** | |
* Retrieves the value for an image attachment's 'sizes' attribute. | |
* | |
* @since 4.4.0 | |
* | |
* @see wp_calculate_image_sizes() | |
* | |
* @param int $attachment_id Image attachment ID. | |
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array of | |
* width and height values in pixels (in that order). Default 'medium'. | |
* @param array $image_meta Optional. The image meta data as returned by 'wp_get_attachment_metadata()'. | |
* Default null. | |
* @return string|false A valid source size value for use in a 'sizes' attribute or false. | |
*/ | |
function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $image_meta = null ) { | |
$image = wp_get_attachment_image_src( $attachment_id, $size ); | |
if ( ! $image ) { | |
return false; | |
} | |
if ( ! is_array( $image_meta ) ) { | |
$image_meta = wp_get_attachment_metadata( $attachment_id ); | |
} | |
$image_src = $image[0]; | |
$size_array = array( | |
absint( $image[1] ), | |
absint( $image[2] ), | |
); | |
return wp_calculate_image_sizes( $size_array, $image_src, $image_meta, $attachment_id ); | |
} | |
/** | |
* Creates a 'sizes' attribute value for an image. | |
* | |
* @since 4.4.0 | |
* | |
* @param string|int[] $size Image size. Accepts any registered image size name, or an array of | |
* width and height values in pixels (in that order). | |
* @param string $image_src Optional. The URL to the image file. Default null. | |
* @param array $image_meta Optional. The image meta data as returned by 'wp_get_attachment_metadata()'. | |
* Default null. | |
* @param int $attachment_id Optional. Image attachment ID. Either `$image_meta` or `$attachment_id` | |
* is needed when using the image size name as argument for `$size`. Default 0. | |
* @return string|false A valid source size value for use in a 'sizes' attribute or false. | |
*/ | |
function wp_calculate_image_sizes( $size, $image_src = null, $image_meta = null, $attachment_id = 0 ) { | |
$width = 0; | |
if ( is_array( $size ) ) { | |
$width = absint( $size[0] ); | |
} elseif ( is_string( $size ) ) { | |
if ( ! $image_meta && $attachment_id ) { | |
$image_meta = wp_get_attachment_metadata( $attachment_id ); | |
} | |
if ( is_array( $image_meta ) ) { | |
$size_array = _wp_get_image_size_from_meta( $size, $image_meta ); | |
if ( $size_array ) { | |
$width = absint( $size_array[0] ); | |
} | |
} | |
} | |
if ( ! $width ) { | |
return false; | |
} | |
// Setup the default 'sizes' attribute. | |
$sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $width ); | |
/** | |
* Filters the output of 'wp_calculate_image_sizes()'. | |
* | |
* @since 4.4.0 | |
* | |
* @param string $sizes A source size value for use in a 'sizes' attribute. | |
* @param string|int[] $size Requested image size. Can be any registered image size name, or | |
* an array of width and height values in pixels (in that order). | |
* @param string|null $image_src The URL to the image file or null. | |
* @param array|null $image_meta The image meta data as returned by wp_get_attachment_metadata() or null. | |
* @param int $attachment_id Image attachment ID of the original image or 0. | |
*/ | |
return apply_filters( 'wp_calculate_image_sizes', $sizes, $size, $image_src, $image_meta, $attachment_id ); | |
} | |
/** | |
* Determines if the image meta data is for the image source file. | |
* | |
* The image meta data is retrieved by attachment post ID. In some cases the post IDs may change. | |
* For example when the website is exported and imported at another website. Then the | |
* attachment post IDs that are in post_content for the exported website may not match | |
* the same attachments at the new website. | |
* | |
* @since 5.5.0 | |
* | |
* @param string $image_location The full path or URI to the image file. | |
* @param array $image_meta The attachment meta data as returned by 'wp_get_attachment_metadata()'. | |
* @param int $attachment_id Optional. The image attachment ID. Default 0. | |
* @return bool Whether the image meta is for this image file. | |
*/ | |
function wp_image_file_matches_image_meta( $image_location, $image_meta, $attachment_id = 0 ) { | |
$match = false; | |
// Ensure the $image_meta is valid. | |
if ( isset( $image_meta['file'] ) && strlen( $image_meta['file'] ) > 4 ) { | |
// Remove query args in image URI. | |
list( $image_location ) = explode( '?', $image_location ); | |
// Check if the relative image path from the image meta is at the end of $image_location. | |
if ( strrpos( $image_location, $image_meta['file'] ) === strlen( $image_location ) - strlen( $image_meta['file'] ) ) { | |
$match = true; | |
} else { | |
// Retrieve the uploads sub-directory from the full size image. | |
$dirname = _wp_get_attachment_relative_path( $image_meta['file'] ); | |
if ( $dirname ) { | |
$dirname = trailingslashit( $dirname ); | |
} | |
if ( ! empty( $image_meta['original_image'] ) ) { | |
$relative_path = $dirname . $image_meta['original_image']; | |
if ( strrpos( $image_location, $relative_path ) === strlen( $image_location ) - strlen( $relative_path ) ) { | |
$match = true; | |
} | |
} | |
if ( ! $match && ! empty( $image_meta['sizes'] ) ) { | |
foreach ( $image_meta['sizes'] as $image_size_data ) { | |
$relative_path = $dirname . $image_size_data['file']; | |
if ( strrpos( $image_location, $relative_path ) === strlen( $image_location ) - strlen( $relative_path ) ) { | |
$match = true; | |
break; | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Filters whether an image path or URI matches image meta. | |
* | |
* @since 5.5.0 | |
* | |
* @param bool $match Whether the image relative path from the image meta | |
* matches the end of the URI or path to the image file. | |
* @param string $image_location Full path or URI to the tested image file. | |
* @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. | |
* @param int $attachment_id The image attachment ID or 0 if not supplied. | |
*/ | |
return apply_filters( 'wp_image_file_matches_image_meta', $match, $image_location, $image_meta, $attachment_id ); | |
} | |
/** | |
* Determines an image's width and height dimensions based on the source file. | |
* | |
* @since 5.5.0 | |
* | |
* @param string $image_src The image source file. | |
* @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. | |
* @param int $attachment_id Optional. The image attachment ID. Default 0. | |
* @return array|false Array with first element being the width and second element being the height, | |
* or false if dimensions cannot be determined. | |
*/ | |
function wp_image_src_get_dimensions( $image_src, $image_meta, $attachment_id = 0 ) { | |
$dimensions = false; | |
// Is it a full size image? | |
if ( | |
isset( $image_meta['file'] ) && | |
strpos( $image_src, wp_basename( $image_meta['file'] ) ) !== false | |
) { | |
$dimensions = array( | |
(int) $image_meta['width'], | |
(int) $image_meta['height'], | |
); | |
} | |
if ( ! $dimensions && ! empty( $image_meta['sizes'] ) ) { | |
$src_filename = wp_basename( $image_src ); | |
foreach ( $image_meta['sizes'] as $image_size_data ) { | |
if ( $src_filename === $image_size_data['file'] ) { | |
$dimensions = array( | |
(int) $image_size_data['width'], | |
(int) $image_size_data['height'], | |
); | |
break; | |
} | |
} | |
} | |
/** | |
* Filters the 'wp_image_src_get_dimensions' value. | |
* | |
* @since 5.7.0 | |
* | |
* @param array|false $dimensions Array with first element being the width | |
* and second element being the height, or | |
* false if dimensions could not be determined. | |
* @param string $image_src The image source file. | |
* @param array $image_meta The image meta data as returned by | |
* 'wp_get_attachment_metadata()'. | |
* @param int $attachment_id The image attachment ID. Default 0. | |
*/ | |
return apply_filters( 'wp_image_src_get_dimensions', $dimensions, $image_src, $image_meta, $attachment_id ); | |
} | |
/** | |
* Adds 'srcset' and 'sizes' attributes to an existing 'img' element. | |
* | |
* @since 4.4.0 | |
* | |
* @see wp_calculate_image_srcset() | |
* @see wp_calculate_image_sizes() | |
* | |
* @param string $image An HTML 'img' element to be filtered. | |
* @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'. | |
* @param int $attachment_id Image attachment ID. | |
* @return string Converted 'img' element with 'srcset' and 'sizes' attributes added. | |
*/ | |
function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) { | |
// Ensure the image meta exists. | |
if ( empty( $image_meta['sizes'] ) ) { | |
return $image; | |
} | |
$image_src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : ''; | |
list( $image_src ) = explode( '?', $image_src ); | |
// Return early if we couldn't get the image source. | |
if ( ! $image_src ) { | |
return $image; | |
} | |
// Bail early if an image has been inserted and later edited. | |
if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash ) && | |
strpos( wp_basename( $image_src ), $img_edit_hash[0] ) === false ) { | |
return $image; | |
} | |
$width = preg_match( '/ width="([0-9]+)"/', $image, $match_width ) ? (int) $match_width[1] : 0; | |
$height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0; | |
if ( $width && $height ) { | |
$size_array = array( $width, $height ); | |
} else { | |
$size_array = wp_image_src_get_dimensions( $image_src, $image_meta, $attachment_id ); | |
if ( ! $size_array ) { | |
return $image; | |
} | |
} | |
$srcset = wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id ); | |
if ( $srcset ) { | |
// Check if there is already a 'sizes' attribute. | |
$sizes = strpos( $image, ' sizes=' ); | |
if ( ! $sizes ) { | |
$sizes = wp_calculate_image_sizes( $size_array, $image_src, $image_meta, $attachment_id ); | |
} | |
} | |
if ( $srcset && $sizes ) { | |
// Format the 'srcset' and 'sizes' string and escape attributes. | |
$attr = sprintf( ' srcset="%s"', esc_attr( $srcset ) ); | |
if ( is_string( $sizes ) ) { | |
$attr .= sprintf( ' sizes="%s"', esc_attr( $sizes ) ); | |
} | |
// Add the srcset and sizes attributes to the image markup. | |
return preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $attr . ' />', $image ); | |
} | |
return $image; | |
} | |
/** | |
* Determines whether to add the `loading` attribute to the specified tag in the specified context. | |
* | |
* @since 5.5.0 | |
* @since 5.7.0 Now returns `true` by default for `iframe` tags. | |
* | |
* @param string $tag_name The tag name. | |
* @param string $context Additional context, like the current filter name | |
* or the function name from where this was called. | |
* @return bool Whether to add the attribute. | |
*/ | |
function wp_lazy_loading_enabled( $tag_name, $context ) { | |
// By default add to all 'img' and 'iframe' tags. | |
// See https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-loading | |
// See https://html.spec.whatwg.org/multipage/iframe-embed-object.html#attr-iframe-loading | |
$default = ( 'img' === $tag_name || 'iframe' === $tag_name ); | |
/** | |
* Filters whether to add the `loading` attribute to the specified tag in the specified context. | |
* | |
* @since 5.5.0 | |
* | |
* @param bool $default Default value. | |
* @param string $tag_name The tag name. | |
* @param string $context Additional context, like the current filter name | |
* or the function name from where this was called. | |
*/ | |
return (bool) apply_filters( 'wp_lazy_loading_enabled', $default, $tag_name, $context ); | |
} | |
/** | |
* Filters specific tags in post content and modifies their markup. | |
* | |
* Modifies HTML tags in post content to include new browser and HTML technologies | |
* that may not have existed at the time of post creation. These modifications currently | |
* include adding `srcset`, `sizes`, and `loading` attributes to `img` HTML tags, as well | |
* as adding `loading` attributes to `iframe` HTML tags. | |
* Future similar optimizations should be added/expected here. | |
* | |
* @since 5.5.0 | |
* @since 5.7.0 Now supports adding `loading` attributes to `iframe` tags. | |
* | |
* @see wp_img_tag_add_width_and_height_attr() | |
* @see wp_img_tag_add_srcset_and_sizes_attr() | |
* @see wp_img_tag_add_loading_attr() | |
* @see wp_iframe_tag_add_loading_attr() | |
* | |
* @param string $content The HTML content to be filtered. | |
* @param string $context Optional. Additional context to pass to the filters. | |
* Defaults to `current_filter()` when not set. | |
* @return string Converted content with images modified. | |
*/ | |
function wp_filter_content_tags( $content, $context = null ) { | |
if ( null === $context ) { | |
$context = current_filter(); | |
} | |
$add_img_loading_attr = wp_lazy_loading_enabled( 'img', $context ); | |
$add_iframe_loading_attr = wp_lazy_loading_enabled( 'iframe', $context ); | |
if ( ! preg_match_all( '/<(img|iframe)\s[^>]+>/', $content, $matches, PREG_SET_ORDER ) ) { | |
return $content; | |
} | |
// List of the unique `img` tags found in $content. | |
$images = array(); | |
// List of the unique `iframe` tags found in $content. | |
$iframes = array(); | |
foreach ( $matches as $match ) { | |
list( $tag, $tag_name ) = $match; | |
switch ( $tag_name ) { | |
case 'img': | |
if ( preg_match( '/wp-image-([0-9]+)/i', $tag, $class_id ) ) { | |
$attachment_id = absint( $class_id[1] ); | |
if ( $attachment_id ) { | |
// If exactly the same image tag is used more than once, overwrite it. | |
// All identical tags will be replaced later with 'str_replace()'. | |
$images[ $tag ] = $attachment_id; | |
break; | |
} | |
} | |
$images[ $tag ] = 0; | |
break; | |
case 'iframe': | |
$iframes[ $tag ] = 0; | |
break; | |
} | |
} | |
// Reduce the array to unique attachment IDs. | |
$attachment_ids = array_unique( array_filter( array_values( $images ) ) ); | |
if ( count( $attachment_ids ) > 1 ) { | |
/* | |
* Warm the object cache with post and meta information for all found | |
* images to avoid making individual database calls. | |
*/ | |
_prime_post_caches( $attachment_ids, false, true ); | |
} | |
// Iterate through the matches in order of occurrence as it is relevant for whether or not to lazy-load. | |
foreach ( $matches as $match ) { | |
// Filter an image match. | |
if ( isset( $images[ $match[0] ] ) ) { | |
$filtered_image = $match[0]; | |
$attachment_id = $images[ $match[0] ]; | |
// Add 'width' and 'height' attributes if applicable. | |
if ( $attachment_id > 0 && false === strpos( $filtered_image, ' width=' ) && false === strpos( $filtered_image, ' height=' ) ) { | |
$filtered_image = wp_img_tag_add_width_and_height_attr( $filtered_image, $context, $attachment_id ); | |
} | |
// Add 'srcset' and 'sizes' attributes if applicable. | |
if ( $attachment_id > 0 && false === strpos( $filtered_image, ' srcset=' ) ) { | |
$filtered_image = wp_img_tag_add_srcset_and_sizes_attr( $filtered_image, $context, $attachment_id ); | |
} | |
// Add 'loading' attribute if applicable. | |
if ( $add_img_loading_attr && false === strpos( $filtered_image, ' loading=' ) ) { | |
$filtered_image = wp_img_tag_add_loading_attr( $filtered_image, $context ); | |
} | |
// Add 'decoding=async' attribute unless a 'decoding' attribute is already present. | |
if ( ! str_contains( $filtered_image, ' decoding=' ) ) { | |
$filtered_image = wp_img_tag_add_decoding_attr( $filtered_image, $context ); | |
} | |
/** | |
* Filters an img tag within the content for a given context. | |
* | |
* @since 6.0.0 | |
* | |
* @param string $filtered_image Full img tag with attributes that will replace the source img tag. | |
* @param string $context Additional context, like the current filter name or the function name from where this was called. | |
* @param int $attachment_id The image attachment ID. May be 0 in case the image is not an attachment. | |
*/ | |
$filtered_image = apply_filters( 'wp_content_img_tag', $filtered_image, $context, $attachment_id ); | |
if ( $filtered_image !== $match[0] ) { | |
$content = str_replace( $match[0], $filtered_image, $content ); | |
} | |
/* | |
* Unset image lookup to not run the same logic again unnecessarily if the same image tag is used more than | |
* once in the same blob of content. | |
*/ | |
unset( $images[ $match[0] ] ); | |
} | |
// Filter an iframe match. | |
if ( isset( $iframes[ $match[0] ] ) ) { | |
$filtered_iframe = $match[0]; | |
// Add 'loading' attribute if applicable. | |
if ( $add_iframe_loading_attr && false === strpos( $filtered_iframe, ' loading=' ) ) { | |
$filtered_iframe = wp_iframe_tag_add_loading_attr( $filtered_iframe, $context ); | |
} | |
if ( $filtered_iframe !== $match[0] ) { | |
$content = str_replace( $match[0], $filtered_iframe, $content ); | |
} | |
/* | |
* Unset iframe lookup to not run the same logic again unnecessarily if the same iframe tag is used more | |
* than once in the same blob of content. | |
*/ | |
unset( $iframes[ $match[0] ] ); | |
} | |
} | |
return $content; | |
} | |
/** | |
* Adds `loading` attribute to an `img` HTML tag. | |
* | |
* @since 5.5.0 | |
* | |
* @param string $image The HTML `img` tag where the attribute should be added. | |
* @param string $context Additional context to pass to the filters. | |
* @return string Converted `img` tag with `loading` attribute added. | |
*/ | |
function wp_img_tag_add_loading_attr( $image, $context ) { | |
// Get loading attribute value to use. This must occur before the conditional check below so that even images that | |
// are ineligible for being lazy-loaded are considered. | |
$value = wp_get_loading_attr_default( $context ); | |
// Images should have source and dimension attributes for the `loading` attribute to be added. | |
if ( false === strpos( $image, ' src="' ) || false === strpos( $image, ' width="' ) || false === strpos( $image, ' height="' ) ) { | |
return $image; | |
} | |
/** | |
* Filters the `loading` attribute value to add to an image. Default `lazy`. | |
* | |
* Returning `false` or an empty string will not add the attribute. | |
* Returning `true` will add the default value. | |
* | |
* @since 5.5.0 | |
* | |
* @param string|bool $value The `loading` attribute value. Returning a falsey value will result in | |
* the attribute being omitted for the image. | |
* @param string $image The HTML `img` tag to be filtered. | |
* @param string $context Additional context about how the function was called or where the img tag is. | |
*/ | |
$value = apply_filters( 'wp_img_tag_add_loading_attr', $value, $image, $context ); | |
if ( $value ) { | |
if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) { | |
$value = 'lazy'; | |
} | |
return str_replace( '<img', '<img loading="' . esc_attr( $value ) . '"', $image ); | |
} | |
return $image; | |
} | |
/** | |
* Adds `decoding` attribute to an `img` HTML tag. | |
* | |
* The `decoding` attribute allows developers to indicate whether the | |
* browser can decode the image off the main thread (`async`), on the | |
* main thread (`sync`) or as determined by the browser (`auto`). | |
* | |
* By default WordPress adds `decoding="async"` to images but developers | |
* can use the {@see 'wp_img_tag_add_decoding_attr'} filter to modify this | |
* to remove the attribute or set it to another accepted value. | |
* | |
* @since 6.1.0 | |
* | |
* @param string $image The HTML `img` tag where the attribute should be added. | |
* @param string $context Additional context to pass to the filters. | |
* | |
* @return string Converted `img` tag with `decoding` attribute added. | |
*/ | |
function wp_img_tag_add_decoding_attr( $image, $context ) { | |
// Only apply the decoding attribute to images that have a src attribute that | |
// starts with a double quote, ensuring escaped JSON is also excluded. | |
if ( false === strpos( $image, ' src="' ) ) { | |
return $image; | |
} | |
/** | |
* Filters the `decoding` attribute value to add to an image. Default `async`. | |
* | |
* Returning a falsey value will omit the attribute. | |
* | |
* @since 6.1.0 | |
* | |
* @param string|false|null $value The `decoding` attribute value. Returning a falsey value | |
* will result in the attribute being omitted for the image. | |
* Otherwise, it may be: 'async' (default), 'sync', or 'auto'. | |
* @param string $image The HTML `img` tag to be filtered. | |
* @param string $context Additional context about how the function was called | |
* or where the img tag is. | |
*/ | |
$value = apply_filters( 'wp_img_tag_add_decoding_attr', 'async', $image, $context ); | |
if ( in_array( $value, array( 'async', 'sync', 'auto' ), true ) ) { | |
$image = str_replace( '<img ', '<img decoding="' . esc_attr( $value ) . '" ', $image ); | |
} | |
return $image; | |
} | |
/** | |
* Adds `width` and `height` attributes to an `img` HTML tag. | |
* | |
* @since 5.5.0 | |
* | |
* @param string $image The HTML `img` tag where the attribute should be added. | |
* @param string $context Additional context to pass to the filters. | |
* @param int $attachment_id Image attachment ID. | |
* @return string Converted 'img' element with 'width' and 'height' attributes added. | |
*/ | |
function wp_img_tag_add_width_and_height_attr( $image, $context, $attachment_id ) { | |
$image_src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : ''; | |
list( $image_src ) = explode( '?', $image_src ); | |
// Return early if we couldn't get the image source. | |
if ( ! $image_src ) { | |
return $image; | |
} | |
/** | |
* Filters whether to add the missing `width` and `height` HTML attributes to the img tag. Default `true`. | |
* | |
* Returning anything else than `true` will not add the attributes. | |
* | |
* @since 5.5.0 | |
* | |
* @param bool $value The filtered value, defaults to `true`. | |
* @param string $image The HTML `img` tag where the attribute should be added. | |
* @param string $context Additional context about how the function was called or where the img tag is. | |
* @param int $attachment_id The image attachment ID. | |
*/ | |
$add = apply_filters( 'wp_img_tag_add_width_and_height_attr', true, $image, $context, $attachment_id ); | |
if ( true === $add ) { | |
$image_meta = wp_get_attachment_metadata( $attachment_id ); | |
$size_array = wp_image_src_get_dimensions( $image_src, $image_meta, $attachment_id ); | |
if ( $size_array ) { | |
$hw = trim( image_hwstring( $size_array[0], $size_array[1] ) ); | |
return str_replace( '<img', "<img {$hw}", $image ); | |
} | |
} | |
return $image; | |
} | |
/** | |
* Adds `srcset` and `sizes` attributes to an existing `img` HTML tag. | |
* | |
* @since 5.5.0 | |
* | |
* @param string $image The HTML `img` tag where the attribute should be added. | |
* @param string $context Additional context to pass to the filters. | |
* @param int $attachment_id Image attachment ID. | |
* @return string Converted 'img' element with 'loading' attribute added. | |
*/ | |
function wp_img_tag_add_srcset_and_sizes_attr( $image, $context, $attachment_id ) { | |
/** | |
* Filters whether to add the `srcset` and `sizes` HTML attributes to the img tag. Default `true`. | |
* | |
* Returning anything else than `true` will not add the attributes. | |
* | |
* @since 5.5.0 | |
* | |
* @param bool $value The filtered value, defaults to `true`. | |
* @param string $image The HTML `img` tag where the attribute should be added. | |
* @param string $context Additional context about how the function was called or where the img tag is. | |
* @param int $attachment_id The image attachment ID. | |
*/ | |
$add = apply_filters( 'wp_img_tag_add_srcset_and_sizes_attr', true, $image, $context, $attachment_id ); | |
if ( true === $add ) { | |
$image_meta = wp_get_attachment_metadata( $attachment_id ); | |
return wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ); | |
} | |
return $image; | |
} | |
/** | |
* Adds `loading` attribute to an `iframe` HTML tag. | |
* | |
* @since 5.7.0 | |
* | |
* @param string $iframe The HTML `iframe` tag where the attribute should be added. | |
* @param string $context Additional context to pass to the filters. | |
* @return string Converted `iframe` tag with `loading` attribute added. | |
*/ | |
function wp_iframe_tag_add_loading_attr( $iframe, $context ) { | |
// Iframes with fallback content (see `wp_filter_oembed_result()`) should not be lazy-loaded because they are | |
// visually hidden initially. | |
if ( false !== strpos( $iframe, ' data-secret="' ) ) { | |
return $iframe; | |
} | |
// Get loading attribute value to use. This must occur before the conditional check below so that even iframes that | |
// are ineligible for being lazy-loaded are considered. | |
$value = wp_get_loading_attr_default( $context ); | |
// Iframes should have source and dimension attributes for the `loading` attribute to be added. | |
if ( false === strpos( $iframe, ' src="' ) || false === strpos( $iframe, ' width="' ) || false === strpos( $iframe, ' height="' ) ) { | |
return $iframe; | |
} | |
/** | |
* Filters the `loading` attribute value to add to an iframe. Default `lazy`. | |
* | |
* Returning `false` or an empty string will not add the attribute. | |
* Returning `true` will add the default value. | |
* | |
* @since 5.7.0 | |
* | |
* @param string|bool $value The `loading` attribute value. Returning a falsey value will result in | |
* the attribute being omitted for the iframe. | |
* @param string $iframe The HTML `iframe` tag to be filtered. | |
* @param string $context Additional context about how the function was called or where the iframe tag is. | |
*/ | |
$value = apply_filters( 'wp_iframe_tag_add_loading_attr', $value, $iframe, $context ); | |
if ( $value ) { | |
if ( ! in_array( $value, array( 'lazy', 'eager' ), true ) ) { | |
$value = 'lazy'; | |
} | |
return str_replace( '<iframe', '<iframe loading="' . esc_attr( $value ) . '"', $iframe ); | |
} | |
return $iframe; | |
} | |
/** | |
* Adds a 'wp-post-image' class to post thumbnails. Internal use only. | |
* | |
* Uses the {@see 'begin_fetch_post_thumbnail_html'} and {@see 'end_fetch_post_thumbnail_html'} | |
* action hooks to dynamically add/remove itself so as to only filter post thumbnails. | |
* | |
* @ignore | |
* @since 2.9.0 | |
* | |
* @param string[] $attr Array of thumbnail attributes including src, class, alt, title, keyed by attribute name. | |
* @return string[] Modified array of attributes including the new 'wp-post-image' class. | |
*/ | |
function _wp_post_thumbnail_class_filter( $attr ) { | |
$attr['class'] .= ' wp-post-image'; | |
return $attr; | |
} | |
/** | |
* Adds '_wp_post_thumbnail_class_filter' callback to the 'wp_get_attachment_image_attributes' | |
* filter hook. Internal use only. | |
* | |
* @ignore | |
* @since 2.9.0 | |
* | |
* @param string[] $attr Array of thumbnail attributes including src, class, alt, title, keyed by attribute name. | |
*/ | |
function _wp_post_thumbnail_class_filter_add( $attr ) { | |
add_filter( 'wp_get_attachment_image_attributes', '_wp_post_thumbnail_class_filter' ); | |
} | |
/** | |
* Removes the '_wp_post_thumbnail_class_filter' callback from the 'wp_get_attachment_image_attributes' | |
* filter hook. Internal use only. | |
* | |
* @ignore | |
* @since 2.9.0 | |
* | |
* @param string[] $attr Array of thumbnail attributes including src, class, alt, title, keyed by attribute name. | |
*/ | |
function _wp_post_thumbnail_class_filter_remove( $attr ) { | |
remove_filter( 'wp_get_attachment_image_attributes', '_wp_post_thumbnail_class_filter' ); | |
} | |
add_shortcode( 'wp_caption', 'img_caption_shortcode' ); | |
add_shortcode( 'caption', 'img_caption_shortcode' ); | |
/** | |
* Builds the Caption shortcode output. | |
* | |
* Allows a plugin to replace the content that would otherwise be returned. The | |
* filter is {@see 'img_caption_shortcode'} and passes an empty string, the attr | |
* parameter and the content parameter values. | |
* | |
* The supported attributes for the shortcode are 'id', 'caption_id', 'align', | |
* 'width', 'caption', and 'class'. | |
* | |
* @since 2.6.0 | |
* @since 3.9.0 The `class` attribute was added. | |
* @since 5.1.0 The `caption_id` attribute was added. | |
* @since 5.9.0 The `$content` parameter default value changed from `null` to `''`. | |
* | |
* @param array $attr { | |
* Attributes of the caption shortcode. | |
* | |
* @type string $id ID of the image and caption container element, i.e. `<figure>` or `<div>`. | |
* @type string $caption_id ID of the caption element, i.e. `<figcaption>` or `<p>`. | |
* @type string $align Class name that aligns the caption. Default 'alignnone'. Accepts 'alignleft', | |
* 'aligncenter', alignright', 'alignnone'. | |
* @type int $width The width of the caption, in pixels. | |
* @type string $caption The caption text. | |
* @type string $class Additional class name(s) added to the caption container. | |
* } | |
* @param string $content Optional. Shortcode content. Default empty string. | |
* @return string HTML content to display the caption. | |
*/ | |
function img_caption_shortcode( $attr, $content = '' ) { | |
// New-style shortcode with the caption inside the shortcode with the link and image tags. | |
if ( ! isset( $attr['caption'] ) ) { | |
if ( preg_match( '#((?:<a [^>]+>\s*)?<img [^>]+>(?:\s*</a>)?)(.*)#is', $content, $matches ) ) { | |
$content = $matches[1]; | |
$attr['caption'] = trim( $matches[2] ); | |
} | |
} elseif ( strpos( $attr['caption'], '<' ) !== false ) { | |
$attr['caption'] = wp_kses( $attr['caption'], 'post' ); | |
} | |
/** | |
* Filters the default caption shortcode output. | |
* | |
* If the filtered output isn't empty, it will be used instead of generating | |
* the default caption template. | |
* | |
* @since 2.6.0 | |
* | |
* @see img_caption_shortcode() | |
* | |
* @param string $output The caption output. Default empty. | |
* @param array $attr Attributes of the caption shortcode. | |
* @param string $content The image element, possibly wrapped in a hyperlink. | |
*/ | |
$output = apply_filters( 'img_caption_shortcode', '', $attr, $content ); | |
if ( ! empty( $output ) ) { | |
return $output; | |
} | |
$atts = shortcode_atts( | |
array( | |
'id' => '', | |
'caption_id' => '', | |
'align' => 'alignnone', | |
'width' => '', | |
'caption' => '', | |
'class' => '', | |
), | |
$attr, | |
'caption' | |
); | |
$atts['width'] = (int) $atts['width']; | |
if ( $atts['width'] < 1 || empty( $atts['caption'] ) ) { | |
return $content; | |
} | |
$id = ''; | |
$caption_id = ''; | |
$describedby = ''; | |
if ( $atts['id'] ) { | |
$atts['id'] = sanitize_html_class( $atts['id'] ); | |
$id = 'id="' . esc_attr( $atts['id'] ) . '" '; | |
} | |
if ( $atts['caption_id'] ) { | |
$atts['caption_id'] = sanitize_html_class( $atts['caption_id'] ); | |
} elseif ( $atts['id'] ) { | |
$atts['caption_id'] = 'caption-' . str_replace( '_', '-', $atts['id'] ); | |
} | |
if ( $atts['caption_id'] ) { | |
$caption_id = 'id="' . esc_attr( $atts['caption_id'] ) . '" '; | |
$describedby = 'aria-describedby="' . esc_attr( $atts['caption_id'] ) . '" '; | |
} | |
$class = trim( 'wp-caption ' . $atts['align'] . ' ' . $atts['class'] ); | |
$html5 = current_theme_supports( 'html5', 'caption' ); | |
// HTML5 captions never added the extra 10px to the image width. | |
$width = $html5 ? $atts['width'] : ( 10 + $atts['width'] ); | |
/** | |
* Filters the width of an image's caption. | |
* | |
* By default, the caption is 10 pixels greater than the width of the image, | |
* to prevent post content from running up against a floated image. | |
* | |
* @since 3.7.0 | |
* | |
* @see img_caption_shortcode() | |
* | |
* @param int $width Width of the caption in pixels. To remove this inline style, | |
* return zero. | |
* @param array $atts Attributes of the caption shortcode. | |
* @param string $content The image element, possibly wrapped in a hyperlink. | |
*/ | |
$caption_width = apply_filters( 'img_caption_shortcode_width', $width, $atts, $content ); | |
$style = ''; | |
if ( $caption_width ) { | |
$style = 'style="width: ' . (int) $caption_width . 'px" '; | |
} | |
if ( $html5 ) { | |
$html = sprintf( | |
'<figure %s%s%sclass="%s">%s%s</figure>', | |
$id, | |
$describedby, | |
$style, | |
esc_attr( $class ), | |
do_shortcode( $content ), | |
sprintf( | |
'<figcaption %sclass="wp-caption-text">%s</figcaption>', | |
$caption_id, | |
$atts['caption'] | |
) | |
); | |
} else { | |
$html = sprintf( | |
'<div %s%sclass="%s">%s%s</div>', | |
$id, | |
$style, | |
esc_attr( $class ), | |
str_replace( '<img ', '<img ' . $describedby, do_shortcode( $content ) ), | |
sprintf( | |
'<p %sclass="wp-caption-text">%s</p>', | |
$caption_id, | |
$atts['caption'] | |
) | |
); | |
} | |
return $html; | |
} | |
add_shortcode( 'gallery', 'gallery_shortcode' ); | |
/** | |
* Builds the Gallery shortcode output. | |
* | |
* This implements the functionality of the Gallery Shortcode for displaying | |
* WordPress images on a post. | |
* | |
* @since 2.5.0 | |
* @since 2.8.0 Added the `$attr` parameter to set the shortcode output. New attributes included | |
* such as `size`, `itemtag`, `icontag`, `captiontag`, and columns. Changed markup from | |
* `div` tags to `dl`, `dt` and `dd` tags. Support more than one gallery on the | |
* same page. | |
* @since 2.9.0 Added support for `include` and `exclude` to shortcode. | |
* @since 3.5.0 Use get_post() instead of global `$post`. Handle mapping of `ids` to `include` | |
* and `orderby`. | |
* @since 3.6.0 Added validation for tags used in gallery shortcode. Add orientation information to items. | |
* @since 3.7.0 Introduced the `link` attribute. | |
* @since 3.9.0 `html5` gallery support, accepting 'itemtag', 'icontag', and 'captiontag' attributes. | |
* @since 4.0.0 Removed use of `extract()`. | |
* @since 4.1.0 Added attribute to `wp_get_attachment_link()` to output `aria-describedby`. | |
* @since 4.2.0 Passed the shortcode instance ID to `post_gallery` and `post_playlist` filters. | |
* @since 4.6.0 Standardized filter docs to match documentation standards for PHP. | |
* @since 5.1.0 Code cleanup for WPCS 1.0.0 coding standards. | |
* @since 5.3.0 Saved progress of intermediate image creation after upload. | |
* @since 5.5.0 Ensured that galleries can be output as a list of links in feeds. | |
* @since 5.6.0 Replaced order-style PHP type conversion functions with typecasts. Fix logic for | |
* an array of image dimensions. | |
* | |
* @param array $attr { | |
* Attributes of the gallery shortcode. | |
* | |
* @type string $order Order of the images in the gallery. Default 'ASC'. Accepts 'ASC', 'DESC'. | |
* @type string $orderby The field to use when ordering the images. Default 'menu_order ID'. | |
* Accepts any valid SQL ORDERBY statement. | |
* @type int $id Post ID. | |
* @type string $itemtag HTML tag to use for each image in the gallery. | |
* Default 'dl', or 'figure' when the theme registers HTML5 gallery support. | |
* @type string $icontag HTML tag to use for each image's icon. | |
* Default 'dt', or 'div' when the theme registers HTML5 gallery support. | |
* @type string $captiontag HTML tag to use for each image's caption. | |
* Default 'dd', or 'figcaption' when the theme registers HTML5 gallery support. | |
* @type int $columns Number of columns of images to display. Default 3. | |
* @type string|int[] $size Size of the images to display. Accepts any registered image size name, or an array | |
* of width and height values in pixels (in that order). Default 'thumbnail'. | |
* @type string $ids A comma-separated list of IDs of attachments to display. Default empty. | |
* @type string $include A comma-separated list of IDs of attachments to include. Default empty. | |
* @type string $exclude A comma-separated list of IDs of attachments to exclude. Default empty. | |
* @type string $link What to link each image to. Default empty (links to the attachment page). | |
* Accepts 'file', 'none'. | |
* } | |
* @return string HTML content to display gallery. | |
*/ | |
function gallery_shortcode( $attr ) { | |
$post = get_post(); | |
static $instance = 0; | |
$instance++; | |
if ( ! empty( $attr['ids'] ) ) { | |
// 'ids' is explicitly ordered, unless you specify otherwise. | |
if ( empty( $attr['orderby'] ) ) { | |
$attr['orderby'] = 'post__in'; | |
} | |
$attr['include'] = $attr['ids']; | |
} | |
/** | |
* Filters the default gallery shortcode output. | |
* | |
* If the filtered output isn't empty, it will be used instead of generating | |
* the default gallery template. | |
* | |
* @since 2.5.0 | |
* @since 4.2.0 The `$instance` parameter was added. | |
* | |
* @see gallery_shortcode() | |
* | |
* @param string $output The gallery output. Default empty. | |
* @param array $attr Attributes of the gallery shortcode. | |
* @param int $instance Unique numeric ID of this gallery shortcode instance. | |
*/ | |
$output = apply_filters( 'post_gallery', '', $attr, $instance ); | |
if ( ! empty( $output ) ) { | |
return $output; | |
} | |
$html5 = current_theme_supports( 'html5', 'gallery' ); | |
$atts = shortcode_atts( | |
array( | |
'order' => 'ASC', | |
'orderby' => 'menu_order ID', | |
'id' => $post ? $post->ID : 0, | |
'itemtag' => $html5 ? 'figure' : 'dl', | |
'icontag' => $html5 ? 'div' : 'dt', | |
'captiontag' => $html5 ? 'figcaption' : 'dd', | |
'columns' => 3, | |
'size' => 'thumbnail', | |
'include' => '', | |
'exclude' => '', | |
'link' => '', | |
), | |
$attr, | |
'gallery' | |
); | |
$id = (int) $atts['id']; | |
if ( ! empty( $atts['include'] ) ) { | |
$_attachments = get_posts( | |
array( | |
'include' => $atts['include'], | |
'post_status' => 'inherit', | |
'post_type' => 'attachment', | |
'post_mime_type' => 'image', | |
'order' => $atts['order'], | |
'orderby' => $atts['orderby'], | |
) | |
); | |
$attachments = array(); | |
foreach ( $_attachments as $key => $val ) { | |
$attachments[ $val->ID ] = $_attachments[ $key ]; | |
} | |
} elseif ( ! empty( $atts['exclude'] ) ) { | |
$attachments = get_children( | |
array( | |
'post_parent' => $id, | |
'exclude' => $atts['exclude'], | |
'post_status' => 'inherit', | |
'post_type' => 'attachment', | |
'post_mime_type' => 'image', | |
'order' => $atts['order'], | |
'orderby' => $atts['orderby'], | |
) | |
); | |
} else { | |
$attachments = get_children( | |
array( | |
'post_parent' => $id, | |
'post_status' => 'inherit', | |
'post_type' => 'attachment', | |
'post_mime_type' => 'image', | |
'order' => $atts['order'], | |
'orderby' => $atts['orderby'], | |
) | |
); | |
} | |
if ( empty( $attachments ) ) { | |
return ''; | |
} | |
if ( is_feed() ) { | |
$output = "\n"; | |
foreach ( $attachments as $att_id => $attachment ) { | |
if ( ! empty( $atts['link'] ) ) { | |
if ( 'none' === $atts['link'] ) { | |
$output .= wp_get_attachment_image( $att_id, $atts['size'], false, $attr ); | |
} else { | |
$output .= wp_get_attachment_link( $att_id, $atts['size'], false ); | |
} | |
} else { | |
$output .= wp_get_attachment_link( $att_id, $atts['size'], true ); | |
} | |
$output .= "\n"; | |
} | |
return $output; | |
} | |
$itemtag = tag_escape( $atts['itemtag'] ); | |
$captiontag = tag_escape( $atts['captiontag'] ); | |
$icontag = tag_escape( $atts['icontag'] ); | |
$valid_tags = wp_kses_allowed_html( 'post' ); | |
if ( ! isset( $valid_tags[ $itemtag ] ) ) { | |
$itemtag = 'dl'; | |
} | |
if ( ! isset( $valid_tags[ $captiontag ] ) ) { | |
$captiontag = 'dd'; | |
} | |
if ( ! isset( $valid_tags[ $icontag ] ) ) { | |
$icontag = 'dt'; | |
} | |
$columns = (int) $atts['columns']; | |
$itemwidth = $columns > 0 ? floor( 100 / $columns ) : 100; | |
$float = is_rtl() ? 'right' : 'left'; | |
$selector = "gallery-{$instance}"; | |
$gallery_style = ''; | |
/** | |
* Filters whether to print default gallery styles. | |
* | |
* @since 3.1.0 | |
* | |
* @param bool $print Whether to print default gallery styles. | |
* Defaults to false if the theme supports HTML5 galleries. | |
* Otherwise, defaults to true. | |
*/ | |
if ( apply_filters( 'use_default_gallery_style', ! $html5 ) ) { | |
$type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"'; | |
$gallery_style = " | |
<style{$type_attr}> | |
#{$selector} { | |
margin: auto; | |
} | |
#{$selector} .gallery-item { | |
float: {$float}; | |
margin-top: 10px; | |
text-align: center; | |
width: {$itemwidth}%; | |
} | |
#{$selector} img { | |
border: 2px solid #cfcfcf; | |
} | |
#{$selector} .gallery-caption { | |
margin-left: 0; | |
} | |
/* see gallery_shortcode() in wp-includes/media.php */ | |
</style>\n\t\t"; | |
} | |
$size_class = sanitize_html_class( is_array( $atts['size'] ) ? implode( 'x', $atts['size'] ) : $atts['size'] ); | |
$gallery_div = "<div id='$selector' class='gallery galleryid-{$id} gallery-columns-{$columns} gallery-size-{$size_class}'>"; | |
/** | |
* Filters the default gallery shortcode CSS styles. | |
* | |
* @since 2.5.0 | |
* | |
* @param string $gallery_style Default CSS styles and opening HTML div container | |
* for the gallery shortcode output. | |
*/ | |
$output = apply_filters( 'gallery_style', $gallery_style . $gallery_div ); | |
$i = 0; | |
foreach ( $attachments as $id => $attachment ) { | |
$attr = ( trim( $attachment->post_excerpt ) ) ? array( 'aria-describedby' => "$selector-$id" ) : ''; | |
if ( ! empty( $atts['link'] ) && 'file' === $atts['link'] ) { | |
$image_output = wp_get_attachment_link( $id, $atts['size'], false, false, false, $attr ); | |
} elseif ( ! empty( $atts['link'] ) && 'none' === $atts['link'] ) { | |
$image_output = wp_get_attachment_image( $id, $atts['size'], false, $attr ); | |
} else { | |
$image_output = wp_get_attachment_link( $id, $atts['size'], true, false, false, $attr ); | |
} | |
$image_meta = wp_get_attachment_metadata( $id ); | |
$orientation = ''; | |
if ( isset( $image_meta['height'], $image_meta['width'] ) ) { | |
$orientation = ( $image_meta['height'] > $image_meta['width'] ) ? 'portrait' : 'landscape'; | |
} | |
$output .= "<{$itemtag} class='gallery-item'>"; | |
$output .= " | |
<{$icontag} class='gallery-icon {$orientation}'> | |
$image_output | |
</{$icontag}>"; | |
if ( $captiontag && trim( $attachment->post_excerpt ) ) { | |
$output .= " | |
<{$captiontag} class='wp-caption-text gallery-caption' id='$selector-$id'> | |
" . wptexturize( $attachment->post_excerpt ) . " | |
</{$captiontag}>"; | |
} | |
$output .= "</{$itemtag}>"; | |
if ( ! $html5 && $columns > 0 && 0 === ++$i % $columns ) { | |
$output .= '<br style="clear: both" />'; | |
} | |
} | |
if ( ! $html5 && $columns > 0 && 0 !== $i % $columns ) { | |
$output .= " | |
<br style='clear: both' />"; | |
} | |
$output .= " | |
</div>\n"; | |
return $output; | |
} | |
/** | |
* Outputs the templates used by playlists. | |
* | |
* @since 3.9.0 | |
*/ | |
function wp_underscore_playlist_templates() { | |
?> | |
<script type="text/html" id="tmpl-wp-playlist-current-item"> | |
<# if ( data.thumb && data.thumb.src ) { #> | |
<img src="{{ data.thumb.src }}" alt="" /> | |
<# } #> | |
<div class="wp-playlist-caption"> | |
<span class="wp-playlist-item-meta wp-playlist-item-title"> | |
<# if ( data.meta.album || data.meta.artist ) { #> | |
<?php | |
/* translators: %s: Playlist item title. */ | |
printf( _x( '“%s”', 'playlist item title' ), '{{ data.title }}' ); | |
?> | |
<# } else { #> | |
{{ data.title }} | |
<# } #> | |
</span> | |
<# if ( data.meta.album ) { #><span class="wp-playlist-item-meta wp-playlist-item-album">{{ data.meta.album }}</span><# } #> | |
<# if ( data.meta.artist ) { #><span class="wp-playlist-item-meta wp-playlist-item-artist">{{ data.meta.artist }}</span><# } #> | |
</div> | |
</script> | |
<script type="text/html" id="tmpl-wp-playlist-item"> | |
<div class="wp-playlist-item"> | |
<a class="wp-playlist-caption" href="{{ data.src }}"> | |
{{ data.index ? ( data.index + '. ' ) : '' }} | |
<# if ( data.caption ) { #> | |
{{ data.caption }} | |
<# } else { #> | |
<# if ( data.artists && data.meta.artist ) { #> | |
<span class="wp-playlist-item-title"> | |
<?php | |
/* translators: %s: Playlist item title. */ | |
printf( _x( '“%s”', 'playlist item title' ), '{{{ data.title }}}' ); | |
?> | |
</span> | |
<span class="wp-playlist-item-artist"> — {{ data.meta.artist }}</span> | |
<# } else { #> | |
<span class="wp-playlist-item-title">{{{ data.title }}}</span> | |
<# } #> | |
<# } #> | |
</a> | |
<# if ( data.meta.length_formatted ) { #> | |
<div class="wp-playlist-item-length">{{ data.meta.length_formatted }}</div> | |
<# } #> | |
</div> | |
</script> | |
<?php | |
} | |
/** | |
* Outputs and enqueues default scripts and styles for playlists. | |
* | |
* @since 3.9.0 | |
* | |
* @param string $type Type of playlist. Accepts 'audio' or 'video'. | |
*/ | |
function wp_playlist_scripts( $type ) { | |
wp_enqueue_style( 'wp-mediaelement' ); | |
wp_enqueue_script( 'wp-playlist' ); | |
?> | |
<!--[if lt IE 9]><script>document.createElement('<?php echo esc_js( $type ); ?>');</script><![endif]--> | |
<?php | |
add_action( 'wp_footer', 'wp_underscore_playlist_templates', 0 ); | |
add_action( 'admin_footer', 'wp_underscore_playlist_templates', 0 ); | |
} | |
/** | |
* Builds the Playlist shortcode output. | |
* | |
* This implements the functionality of the playlist shortcode for displaying | |
* a collection of WordPress audio or video files in a post. | |
* | |
* @since 3.9.0 | |
* | |
* @global int $content_width | |
* | |
* @param array $attr { | |
* Array of default playlist attributes. | |
* | |
* @type string $type Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'. | |
* @type string $order Designates ascending or descending order of items in the playlist. | |
* Accepts 'ASC', 'DESC'. Default 'ASC'. | |
* @type string $orderby Any column, or columns, to sort the playlist. If $ids are | |
* passed, this defaults to the order of the $ids array ('post__in'). | |
* Otherwise default is 'menu_order ID'. | |
* @type int $id If an explicit $ids array is not present, this parameter | |
* will determine which attachments are used for the playlist. | |
* Default is the current post ID. | |
* @type array $ids Create a playlist out of these explicit attachment IDs. If empty, | |
* a playlist will be created from all $type attachments of $id. | |
* Default empty. | |
* @type array $exclude List of specific attachment IDs to exclude from the playlist. Default empty. | |
* @type string $style Playlist style to use. Accepts 'light' or 'dark'. Default 'light'. | |
* @type bool $tracklist Whether to show or hide the playlist. Default true. | |
* @type bool $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true. | |
* @type bool $images Show or hide the video or audio thumbnail (Featured Image/post | |
* thumbnail). Default true. | |
* @type bool $artists Whether to show or hide artist name in the playlist. Default true. | |
* } | |
* | |
* @return string Playlist output. Empty string if the passed type is unsupported. | |
*/ | |
function wp_playlist_shortcode( $attr ) { | |
global $content_width; | |
$post = get_post(); | |
static $instance = 0; | |
$instance++; | |
if ( ! empty( $attr['ids'] ) ) { | |
// 'ids' is explicitly ordered, unless you specify otherwise. | |
if ( empty( $attr['orderby'] ) ) { | |
$attr['orderby'] = 'post__in'; | |
} | |
$attr['include'] = $attr['ids']; | |
} | |
/** | |
* Filters the playlist output. | |
* | |
* Returning a non-empty value from the filter will short-circuit generation | |
* of the default playlist output, returning the passed value instead. | |
* | |
* @since 3.9.0 | |
* @since 4.2.0 The `$instance` parameter was added. | |
* | |
* @param string $output Playlist output. Default empty. | |
* @param array $attr An array of shortcode attributes. | |
* @param int $instance Unique numeric ID of this playlist shortcode instance. | |
*/ | |
$output = apply_filters( 'post_playlist', '', $attr, $instance ); | |
if ( ! empty( $output ) ) { | |
return $output; | |
} | |
$atts = shortcode_atts( | |
array( | |
'type' => 'audio', | |
'order' => 'ASC', | |
'orderby' => 'menu_order ID', | |
'id' => $post ? $post->ID : 0, | |
'include' => '', | |
'exclude' => '', | |
'style' => 'light', | |
'tracklist' => true, | |
'tracknumbers' => true, | |
'images' => true, | |
'artists' => true, | |
), | |
$attr, | |
'playlist' | |
); | |
$id = (int) $atts['id']; | |
if ( 'audio' !== $atts['type'] ) { | |
$atts['type'] = 'video'; | |
} | |
$args = array( | |
'post_status' => 'inherit', | |
'post_type' => 'attachment', | |
'post_mime_type' => $atts['type'], | |
'order' => $atts['order'], | |
'orderby' => $atts['orderby'], | |
); | |
if ( ! empty( $atts['include'] ) ) { | |
$args['include'] = $atts['include']; | |
$_attachments = get_posts( $args ); | |
$attachments = array(); | |
foreach ( $_attachments as $key => $val ) { | |
$attachments[ $val->ID ] = $_attachments[ $key ]; | |
} | |
} elseif ( ! empty( $atts['exclude'] ) ) { | |
$args['post_parent'] = $id; | |
$args['exclude'] = $atts['exclude']; | |
$attachments = get_children( $args ); | |
} else { | |
$args['post_parent'] = $id; | |
$attachments = get_children( $args ); | |
} | |
if ( empty( $attachments ) ) { | |
return ''; | |
} | |
if ( is_feed() ) { | |
$output = "\n"; | |
foreach ( $attachments as $att_id => $attachment ) { | |
$output .= wp_get_attachment_link( $att_id ) . "\n"; | |
} | |
return $output; | |
} | |
$outer = 22; // Default padding and border of wrapper. | |
$default_width = 640; | |
$default_height = 360; | |
$theme_width = empty( $content_width ) ? $default_width : ( $content_width - $outer ); | |
$theme_height = empty( $content_width ) ? $default_height : round( ( $default_height * $theme_width ) / $default_width ); | |
$data = array( | |
'type' => $atts['type'], | |
// Don't pass strings to JSON, will be truthy in JS. | |
'tracklist' => wp_validate_boolean( $atts['tracklist'] ), | |
'tracknumbers' => wp_validate_boolean( $atts['tracknumbers'] ), | |
'images' => wp_validate_boolean( $atts['images'] ), | |
'artists' => wp_validate_boolean( $atts['artists'] ), | |
); | |
$tracks = array(); | |
foreach ( $attachments as $attachment ) { | |
$url = wp_get_attachment_url( $attachment->ID ); | |
$ftype = wp_check_filetype( $url, wp_get_mime_types() ); | |
$track = array( | |
'src' => $url, | |
'type' => $ftype['type'], | |
'title' => $attachment->post_title, | |
'caption' => $attachment->post_excerpt, | |
'description' => $attachment->post_content, | |
); | |
$track['meta'] = array(); | |
$meta = wp_get_attachment_metadata( $attachment->ID ); | |
if ( ! empty( $meta ) ) { | |
foreach ( wp_get_attachment_id3_keys( $attachment ) as $key => $label ) { | |
if ( ! empty( $meta[ $key ] ) ) { | |
$track['meta'][ $key ] = $meta[ $key ]; | |
} | |
} | |
if ( 'video' === $atts['type'] ) { | |
if ( ! empty( $meta['width'] ) && ! empty( $meta['height'] ) ) { | |
$width = $meta['width']; | |
$height = $meta['height']; | |
$theme_height = round( ( $height * $theme_width ) / $width ); | |
} else { | |
$width = $default_width; | |
$height = $default_height; | |
} | |
$track['dimensions'] = array( | |
'original' => compact( 'width', 'height' ), | |
'resized' => array( | |
'width' => $theme_width, | |
'height' => $theme_height, | |
), | |
); | |
} | |
} | |
if ( $atts['images'] ) { | |
$thumb_id = get_post_thumbnail_id( $attachment->ID ); | |
if ( ! empty( $thumb_id ) ) { | |
list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'full' ); | |
$track['image'] = compact( 'src', 'width', 'height' ); | |
list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'thumbnail' ); | |
$track['thumb'] = compact( 'src', 'width', 'height' ); | |
} else { | |
$src = wp_mime_type_icon( $attachment->ID ); | |
$width = 48; | |
$height = 64; | |
$track['image'] = compact( 'src', 'width', 'height' ); | |
$track['thumb'] = compact( 'src', 'width', 'height' ); | |
} | |
} | |
$tracks[] = $track; | |
} | |
$data['tracks'] = $tracks; | |
$safe_type = esc_attr( $atts['type'] ); | |
$safe_style = esc_attr( $atts['style'] ); | |
ob_start(); | |
if ( 1 === $instance ) { | |
/** | |
* Prints and enqueues playlist scripts, styles, and JavaScript templates. | |
* | |
* @since 3.9.0 | |
* | |
* @param string $type Type of playlist. Possible values are 'audio' or 'video'. | |
* @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'. | |
*/ | |
do_action( 'wp_playlist_scripts', $atts['type'], $atts['style'] ); | |
} | |
?> | |
<div class="wp-playlist wp-<?php echo $safe_type; ?>-playlist wp-playlist-<?php echo $safe_style; ?>"> | |
<?php if ( 'audio' === $atts['type'] ) : ?> | |
<div class="wp-playlist-current-item"></div> | |
<?php endif; ?> | |
<<?php echo $safe_type; ?> controls="controls" preload="none" width="<?php echo (int) $theme_width; ?>" | |
<?php | |
if ( 'video' === $safe_type ) { | |
echo ' height="', (int) $theme_height, '"'; | |
} | |
?> | |
></<?php echo $safe_type; ?>> | |
<div class="wp-playlist-next"></div> | |
<div class="wp-playlist-prev"></div> | |
<noscript> | |
<ol> | |
<?php | |
foreach ( $attachments as $att_id => $attachment ) { | |
printf( '<li>%s</li>', wp_get_attachment_link( $att_id ) ); | |
} | |
?> | |
</ol> | |
</noscript> | |
<script type="application/json" class="wp-playlist-script"><?php echo wp_json_encode( $data ); ?></script> | |
</div> | |
<?php | |
return ob_get_clean(); | |
} | |
add_shortcode( 'playlist', 'wp_playlist_shortcode' ); | |
/** | |
* Provides a No-JS Flash fallback as a last resort for audio / video. | |
* | |
* @since 3.6.0 | |
* | |
* @param string $url The media element URL. | |
* @return string Fallback HTML. | |
*/ | |
function wp_mediaelement_fallback( $url ) { | |
/** | |
* Filters the Mediaelement fallback output for no-JS. | |
* | |
* @since 3.6.0 | |
* | |
* @param string $output Fallback output for no-JS. | |
* @param string $url Media file URL. | |
*/ | |
return apply_filters( 'wp_mediaelement_fallback', sprintf( '<a href="%1$s">%1$s</a>', esc_url( $url ) ), $url ); | |
} | |
/** | |
* Returns a filtered list of supported audio formats. | |
* | |
* @since 3.6.0 | |
* | |
* @return string[] Supported audio formats. | |
*/ | |
function wp_get_audio_extensions() { | |
/** | |
* Filters the list of supported audio formats. | |
* | |
* @since 3.6.0 | |
* | |
* @param string[] $extensions An array of supported audio formats. Defaults are | |
* 'mp3', 'ogg', 'flac', 'm4a', 'wav'. | |
*/ | |
return apply_filters( 'wp_audio_extensions', array( 'mp3', 'ogg', 'flac', 'm4a', 'wav' ) ); | |
} | |
/** | |
* Returns useful keys to use to lookup data from an attachment's stored metadata. | |
* | |
* @since 3.9.0 | |
* | |
* @param WP_Post $attachment The current attachment, provided for context. | |
* @param string $context Optional. The context. Accepts 'edit', 'display'. Default 'display'. | |
* @return string[] Key/value pairs of field keys to labels. | |
*/ | |
function wp_get_attachment_id3_keys( $attachment, $context = 'display' ) { | |
$fields = array( | |
'artist' => __( 'Artist' ), | |
'album' => __( 'Album' ), | |
); | |
if ( 'display' === $context ) { | |
$fields['genre'] = __( 'Genre' ); | |
$fields['year'] = __( 'Year' ); | |
$fields['length_formatted'] = _x( 'Length', 'video or audio' ); | |
} elseif ( 'js' === $context ) { | |
$fields['bitrate'] = __( 'Bitrate' ); | |
$fields['bitrate_mode'] = __( 'Bitrate Mode' ); | |
} | |
/** | |
* Filters the editable list of keys to look up data from an attachment's metadata. | |
* | |
* @since 3.9.0 | |
* | |
* @param array $fields Key/value pairs of field keys to labels. | |
* @param WP_Post $attachment Attachment object. | |
* @param string $context The context. Accepts 'edit', 'display'. Default 'display'. | |
*/ | |
return apply_filters( 'wp_get_attachment_id3_keys', $fields, $attachment, $context ); | |
} | |
/** | |
* Builds the Audio shortcode output. | |
* | |
* This implements the functionality of the Audio Shortcode for displaying | |
* WordPress mp3s in a post. | |
* | |
* @since 3.6.0 | |
* | |
* @param array $attr { | |
* Attributes of the audio shortcode. | |
* | |
* @type string $src URL to the source of the audio file. Default empty. | |
* @type string $loop The 'loop' attribute for the `<audio>` element. Default empty. | |
* @type string $autoplay The 'autoplay' attribute for the `<audio>` element. Default empty. | |
* @type string $preload The 'preload' attribute for the `<audio>` element. Default 'none'. | |
* @type string $class The 'class' attribute for the `<audio>` element. Default 'wp-audio-shortcode'. | |
* @type string $style The 'style' attribute for the `<audio>` element. Default 'width: 100%;'. | |
* } | |
* @param string $content Shortcode content. | |
* @return string|void HTML content to display audio. | |
*/ | |
function wp_audio_shortcode( $attr, $content = '' ) { | |
$post_id = get_post() ? get_the_ID() : 0; | |
static $instance = 0; | |
$instance++; | |
/** | |
* Filters the default audio shortcode output. | |
* | |
* If the filtered output isn't empty, it will be used instead of generating the default audio template. | |
* | |
* @since 3.6.0 | |
* | |
* @param string $html Empty variable to be replaced with shortcode markup. | |
* @param array $attr Attributes of the shortcode. @see wp_audio_shortcode() | |
* @param string $content Shortcode content. | |
* @param int $instance Unique numeric ID of this audio shortcode instance. | |
*/ | |
$override = apply_filters( 'wp_audio_shortcode_override', '', $attr, $content, $instance ); | |
if ( '' !== $override ) { | |
return $override; | |
} | |
$audio = null; | |
$default_types = wp_get_audio_extensions(); | |
$defaults_atts = array( | |
'src' => '', | |
'loop' => '', | |
'autoplay' => '', | |
'preload' => 'none', | |
'class' => 'wp-audio-shortcode', | |
'style' => 'width: 100%;', | |
); | |
foreach ( $default_types as $type ) { | |
$defaults_atts[ $type ] = ''; | |
} | |
$atts = shortcode_atts( $defaults_atts, $attr, 'audio' ); | |
$primary = false; | |
if ( ! empty( $atts['src'] ) ) { | |
$type = wp_check_filetype( $atts['src'], wp_get_mime_types() ); | |
if ( ! in_array( strtolower( $type['ext'] ), $default_types, true ) ) { | |
return sprintf( '<a class="wp-embedded-audio" href="%s">%s</a>', esc_url( $atts['src'] ), esc_html( $atts['src'] ) ); | |
} | |
$primary = true; | |
array_unshift( $default_types, 'src' ); | |
} else { | |
foreach ( $default_types as $ext ) { | |
if ( ! empty( $atts[ $ext ] ) ) { | |
$type = wp_check_filetype( $atts[ $ext ], wp_get_mime_types() ); | |
if ( strtolower( $type['ext'] ) === $ext ) { | |
$primary = true; | |
} | |
} | |
} | |
} | |
if ( ! $primary ) { | |
$audios = get_attached_media( 'audio', $post_id ); | |
if ( empty( $audios ) ) { | |
return; | |
} | |
$audio = reset( $audios ); | |
$atts['src'] = wp_get_attachment_url( $audio->ID ); | |
if ( empty( $atts['src'] ) ) { | |
return; | |
} | |
array_unshift( $default_types, 'src' ); | |
} | |
/** | |
* Filters the media library used for the audio shortcode. | |
* | |
* @since 3.6.0 | |
* | |
* @param string $library Media library used for the audio shortcode. | |
*/ | |
$library = apply_filters( 'wp_audio_shortcode_library', 'mediaelement' ); | |
if ( 'mediaelement' === $library && did_action( 'init' ) ) { | |
wp_enqueue_style( 'wp-mediaelement' ); | |
wp_enqueue_script( 'wp-mediaelement' ); | |
} | |
/** | |
* Filters the class attribute for the audio shortcode output container. | |
* | |
* @since 3.6.0 | |
* @since 4.9.0 The `$atts` parameter was added. | |
* | |
* @param string $class CSS class or list of space-separated classes. | |
* @param array $atts Array of audio shortcode attributes. | |
*/ | |
$atts['class'] = apply_filters( 'wp_audio_shortcode_class', $atts['class'], $atts ); | |
$html_atts = array( | |
'class' => $atts['class'], | |
'id' => sprintf( 'audio-%d-%d', $post_id, $instance ), | |
'loop' => wp_validate_boolean( $atts['loop'] ), | |
'autoplay' => wp_validate_boolean( $atts['autoplay'] ), | |
'preload' => $atts['preload'], | |
'style' => $atts['style'], | |
); | |
// These ones should just be omitted altogether if they are blank. | |
foreach ( array( 'loop', 'autoplay', 'preload' ) as $a ) { | |
if ( empty( $html_atts[ $a ] ) ) { | |
unset( $html_atts[ $a ] ); | |
} | |
} | |
$attr_strings = array(); | |
foreach ( $html_atts as $k => $v ) { | |
$attr_strings[] = $k . '="' . esc_attr( $v ) . '"'; | |
} | |
$html = ''; | |
if ( 'mediaelement' === $library && 1 === $instance ) { | |
$html .= "<!--[if lt IE 9]><script>document.createElement('audio');</script><![endif]-->\n"; | |
} | |
$html .= sprintf( '<audio %s controls="controls">', implode( ' ', $attr_strings ) ); | |
$fileurl = ''; | |
$source = '<source type="%s" src="%s" />'; | |
foreach ( $default_types as $fallback ) { | |
if ( ! empty( $atts[ $fallback ] ) ) { | |
if ( empty( $fileurl ) ) { | |
$fileurl = $atts[ $fallback ]; | |
} | |
$type = wp_check_filetype( $atts[ $fallback ], wp_get_mime_types() ); | |
$url = add_query_arg( '_', $instance, $atts[ $fallback ] ); | |
$html .= sprintf( $source, $type['type'], esc_url( $url ) ); | |
} | |
} | |
if ( 'mediaelement' === $library ) { | |
$html .= wp_mediaelement_fallback( $fileurl ); | |
} | |
$html .= '</audio>'; | |
/** | |
* Filters the audio shortcode output. | |
* | |
* @since 3.6.0 | |
* | |
* @param string $html Audio shortcode HTML output. | |
* @param array $atts Array of audio shortcode attributes. | |
* @param string $audio Audio file. | |
* @param int $post_id Post ID. | |
* @param string $library Media library used for the audio shortcode. | |
*/ | |
return apply_filters( 'wp_audio_shortcode', $html, $atts, $audio, $post_id, $library ); | |
} | |
add_shortcode( 'audio', 'wp_audio_shortcode' ); | |
/** | |
* Returns a filtered list of supported video formats. | |
* | |
* @since 3.6.0 | |
* | |
* @return string[] List of supported video formats. | |
*/ | |
function wp_get_video_extensions() { | |
/** | |
* Filters the list of supported video formats. | |
* | |
* @since 3.6.0 | |
* | |
* @param string[] $extensions An array of supported video formats. Defaults are | |
* 'mp4', 'm4v', 'webm', 'ogv', 'flv'. | |
*/ | |
return apply_filters( 'wp_video_extensions', array( 'mp4', 'm4v', 'webm', 'ogv', 'flv' ) ); | |
} | |
/** | |
* Builds the Video shortcode output. | |
* | |
* This implements the functionality of the Video Shortcode for displaying | |
* WordPress mp4s in a post. | |
* | |
* @since 3.6.0 | |
* | |
* @global int $content_width | |
* | |
* @param array $attr { | |
* Attributes of the shortcode. | |
* | |
* @type string $src URL to the source of the video file. Default empty. | |
* @type int $height Height of the video embed in pixels. Default 360. | |
* @type int $width Width of the video embed in pixels. Default $content_width or 640. | |
* @type string $poster The 'poster' attribute for the `<video>` element. Default empty. | |
* @type string $loop The 'loop' attribute for the `<video>` element. Default empty. | |
* @type string $autoplay The 'autoplay' attribute for the `<video>` element. Default empty. | |
* @type string $muted The 'muted' attribute for the `<video>` element. Default false. | |
* @type string $preload The 'preload' attribute for the `<video>` element. | |
* Default 'metadata'. | |
* @type string $class The 'class' attribute for the `<video>` element. | |
* Default 'wp-video-shortcode'. | |
* } | |
* @param string $content Shortcode content. | |
* @return string|void HTML content to display video. | |
*/ | |
function wp_video_shortcode( $attr, $content = '' ) { | |
global $content_width; | |
$post_id = get_post() ? get_the_ID() : 0; | |
static $instance = 0; | |
$instance++; | |
/** | |
* Filters the default video shortcode output. | |
* | |
* If the filtered output isn't empty, it will be used instead of generating | |
* the default video template. | |
* | |
* @since 3.6.0 | |
* | |
* @see wp_video_shortcode() | |
* | |
* @param string $html Empty variable to be replaced with shortcode markup. | |
* @param array $attr Attributes of the shortcode. @see wp_video_shortcode() | |
* @param string $content Video shortcode content. | |
* @param int $instance Unique numeric ID of this video shortcode instance. | |
*/ | |
$override = apply_filters( 'wp_video_shortcode_override', '', $attr, $content, $instance ); | |
if ( '' !== $override ) { | |
return $override; | |
} | |
$video = null; | |
$default_types = wp_get_video_extensions(); | |
$defaults_atts = array( | |
'src' => '', | |
'poster' => '', | |
'loop' => '', | |
'autoplay' => '', | |
'muted' => 'false', | |
'preload' => 'metadata', | |
'width' => 640, | |
'height' => 360, | |
'class' => 'wp-video-shortcode', | |
); | |
foreach ( $default_types as $type ) { | |
$defaults_atts[ $type ] = ''; | |
} | |
$atts = shortcode_atts( $defaults_atts, $attr, 'video' ); | |
if ( is_admin() ) { | |
// Shrink the video so it isn't huge in the admin. | |
if ( $atts['width'] > $defaults_atts['width'] ) { | |
$atts['height'] = round( ( $atts['height'] * $defaults_atts['width'] ) / $atts['width'] ); | |
$atts['width'] = $defaults_atts['width']; | |
} | |
} else { | |
// If the video is bigger than the theme. | |
if ( ! empty( $content_width ) && $atts['width'] > $content_width ) { | |
$atts['height'] = round( ( $atts['height'] * $content_width ) / $atts['width'] ); | |
$atts['width'] = $content_width; | |
} | |
} | |
$is_vimeo = false; | |
$is_youtube = false; | |
$yt_pattern = '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#'; | |
$vimeo_pattern = '#^https?://(.+\.)?vimeo\.com/.*#'; | |
$primary = false; | |
if ( ! empty( $atts['src'] ) ) { | |
$is_vimeo = ( preg_match( $vimeo_pattern, $atts['src'] ) ); | |
$is_youtube = ( preg_match( $yt_pattern, $atts['src'] ) ); | |
if ( ! $is_youtube && ! $is_vimeo ) { | |
$type = wp_check_filetype( $atts['src'], wp_get_mime_types() ); | |
if ( ! in_array( strtolower( $type['ext'] ), $default_types, true ) ) { | |
return sprintf( '<a class="wp-embedded-video" href="%s">%s</a>', esc_url( $atts['src'] ), esc_html( $atts['src'] ) ); | |
} | |
} | |
if ( $is_vimeo ) { | |
wp_enqueue_script( 'mediaelement-vimeo' ); | |
} | |
$primary = true; | |
array_unshift( $default_types, 'src' ); | |
} else { | |
foreach ( $default_types as $ext ) { | |
if ( ! empty( $atts[ $ext ] ) ) { | |
$type = wp_check_filetype( $atts[ $ext ], wp_get_mime_types() ); | |
if ( strtolower( $type['ext'] ) === $ext ) { | |
$primary = true; | |
} | |
} | |
} | |
} | |
if ( ! $primary ) { | |
$videos = get_attached_media( 'video', $post_id ); | |
if ( empty( $videos ) ) { | |
return; | |
} | |
$video = reset( $videos ); | |
$atts['src'] = wp_get_attachment_url( $video->ID ); | |
if ( empty( $atts['src'] ) ) { | |
return; | |
} | |
array_unshift( $default_types, 'src' ); | |
} | |
/** | |
* Filters the media library used for the video shortcode. | |
* | |
* @since 3.6.0 | |
* | |
* @param string $library Media library used for the video shortcode. | |
*/ | |
$library = apply_filters( 'wp_video_shortcode_library', 'mediaelement' ); | |
if ( 'mediaelement' === $library && did_action( 'init' ) ) { | |
wp_enqueue_style( 'wp-mediaelement' ); | |
wp_enqueue_script( 'wp-mediaelement' ); | |
wp_enqueue_script( 'mediaelement-vimeo' ); | |
} | |