This commit is contained in:
2026-07-02 15:54:39 -06:00
commit 9883323161
17470 changed files with 4470592 additions and 0 deletions
@@ -0,0 +1,447 @@
<?php
/**
* Core Image CDN functionality.
*
* It should be available even if Image CDN is not active.
*
* @package automattic/jetpack-image-cdn
*/
namespace Automattic\Jetpack\Image_CDN;
use Automattic\Jetpack\Status;
use Automattic\Jetpack\Status\Host;
/**
* A static class that provides core Image CDN functionality.
*/
class Image_CDN_Core {
/**
* Register hooks.
*/
public static function setup() {
// Add photon compatibility.
require_once __DIR__ . '/compatibility/photon.php';
// Add ActivityPub compatibility.
require_once __DIR__ . '/compatibility/activitypub.php';
// Add Breakdance compatibility.
require_once __DIR__ . '/compatibility/breakdance.php';
/**
* Add an easy way to photon-ize a URL that is safe to call even if Jetpack isn't active.
*
* See: https://jetpack.com/2013/07/11/photon-and-themes/
*/
add_filter( 'jetpack_photon_url', array( __CLASS__, 'cdn_url' ), 10, 3 );
/**
* WordPress.com
*
* If a cropped WP.com-hosted image is the source image, have Photon replicate the crop.
*/
add_filter( 'jetpack_photon_pre_args', array( __CLASS__, 'parse_wpcom_query_args' ), 10, 2 );
add_filter( 'jetpack_photon_skip_for_url', array( __CLASS__, 'banned_domains' ), 9, 2 );
}
/**
* Generates a Photon URL.
*
* @see https://developer.wordpress.com/docs/photon/
*
* @param string $image_url URL to the publicly accessible image you want to manipulate.
* @param array|string $args An array of arguments, e.g. array( 'w' => '300', 'resize' => array( 123, 456 ) ), or in string form (w=123&h=456).
* @param string|null $scheme URL protocol.
* @return string The raw final URL. You should run this through esc_url() before displaying it.
*/
public static function cdn_url( $image_url, $args = array(), $scheme = null ) {
if ( ! is_string( $image_url ) || empty( $image_url ) ) {
return '';
}
$image_url = trim( $image_url );
if ( ! defined( 'IS_WPCOM' ) || ! \IS_WPCOM ) {
/**
* Disables Photon URL processing for local development
*
* @module photon
*
* @since 4.1.0
*
* @param bool false Result of Automattic\Jetpack\Status->is_offline_mode().
*/
if ( true === apply_filters( 'jetpack_photon_development_mode', ( new Status() )->is_offline_mode() ) ) {
return $image_url;
}
}
/**
* Allow specific image URls to avoid going through Photon.
*
* @module photon
*
* @since 3.2.0
*
* @param bool false Should the image be returned as is, without going through Photon. Default to false.
* @param string $image_url Image URL.
* @param array|string $args Array of Photon arguments.
* @param string|null $scheme Image scheme. Default to null.
*/
if ( false !== apply_filters( 'jetpack_photon_skip_for_url', false, $image_url, $args, $scheme ) ) {
return $image_url;
}
/**
* Filter the original image URL before it goes through Photon.
*
* @module photon
*
* @since 1.9.0
*
* @param string $image_url Image URL.
* @param array|string $args Array of Photon arguments.
* @param string|null $scheme Image scheme. Default to null.
*/
$image_url = apply_filters( 'jetpack_photon_pre_image_url', $image_url, $args, $scheme );
/**
* Filter the original Photon image parameters before Photon is applied to an image.
*
* @module photon
*
* @since 1.9.0
*
* @param array|string $args Array of Photon arguments.
* @param string $image_url Image URL.
* @param string|null $scheme Image scheme. Default to null.
*/
$args = apply_filters( 'jetpack_photon_pre_args', $args, $image_url, $scheme );
if ( empty( $image_url ) ) {
return $image_url;
}
$image_url_parts = wp_parse_url( $image_url );
// Unable to parse.
if ( ! is_array( $image_url_parts ) || empty( $image_url_parts['host'] ) || empty( $image_url_parts['path'] ) ) {
return $image_url;
}
// Ensure image extension is acceptable.
if (
! in_array(
strtolower( pathinfo( $image_url_parts['path'], PATHINFO_EXTENSION ) ),
Image_CDN::get_supported_extensions(),
true
)
) {
return $image_url;
}
if ( is_array( $args ) ) {
// Convert values that are arrays into strings.
foreach ( $args as $arg => $value ) {
if ( is_array( $value ) ) {
$args[ $arg ] = implode( ',', $value );
}
}
// Encode values.
// See https://core.trac.wordpress.org/ticket/17923 .
$args = rawurlencode_deep( $args );
}
// Don't photon-ize WPCOM hosted images -- we can serve them up from wpcom directly.
$is_wpcom_image = false;
if ( self::ends_with( strtolower( $image_url_parts['host'] ), '.files.wordpress.com' ) ) {
$is_wpcom_image = true;
if ( isset( $args['ssl'] ) ) {
// Do not send the ssl argument to prevent caching issues.
unset( $args['ssl'] );
}
}
$is_wpcom_private_site = false;
if ( ( new Host() )->is_wpcom_platform() && ( new Status() )->is_private_site() ) {
$is_wpcom_private_site = true;
if ( isset( $args['ssl'] ) ) {
// Do not send the ssl argument to prevent caching issues.
unset( $args['ssl'] );
}
}
// You can't run a Photon URL through Photon again because query strings are stripped.
// So if the image is already a Photon URL, append the new arguments to the existing URL.
// Alternately, if it's a *.files.wordpress.com url or an image on a private WordPress.com Simple site,
// then keep the domain as is.
if (
self::is_cdn_url( $image_url )
|| $is_wpcom_image
|| $is_wpcom_private_site
) {
$photon_url = add_query_arg( $args, $image_url );
return self::cdn_url_scheme( $photon_url, $scheme );
}
/**
* Allow Photon to use query strings as well.
* By default, Photon doesn't support query strings so we ignore them and look only at the path.
* This setting is Photon Server dependent.
*
* @module photon
*
* @since 1.9.0
*
* @param bool false Should images using query strings go through Photon. Default is false.
* @param string $image_url_parts['host'] Image URL's host.
*/
if ( ! apply_filters( 'jetpack_photon_any_extension_for_domain', false, $image_url_parts['host'] ) ) {
// Photon doesn't support query strings so we ignore them and look only at the path.
// However some source images are served via PHP so check the no-query-string extension.
// For future proofing, this is an excluded list of common issues rather than an allow list.
$extension = pathinfo( $image_url_parts['path'], PATHINFO_EXTENSION );
if ( empty( $extension ) || in_array( $extension, array( 'php', 'ashx' ), true ) ) {
return $image_url;
}
}
$image_host_path = $image_url_parts['host'] . static::escape_path( $image_url_parts['path'] );
/**
* Filters the domain used by the Photon module.
*
* @module photon
*
* @since 3.4.2
*
* @param string https://i0.wp.com Domain used by Photon.
* @param string $image_url URL of the image to be photonized.
*/
$photon_domain = apply_filters( 'jetpack_photon_domain', 'https://i0.wp.com', $image_url );
$photon_domain = trailingslashit( esc_url( $photon_domain ) );
$photon_url = $photon_domain . $image_host_path;
/**
* Add query strings to Photon URL.
* By default, Photon doesn't support query strings so we ignore them.
* This setting is Photon Server dependent.
*
* @module photon
*
* @since 1.9.0
*
* @param bool false Should query strings be added to the image URL. Default is false.
* @param string $image_url_parts['host'] Image URL's host.
*/
if ( isset( $image_url_parts['query'] ) && apply_filters( 'jetpack_photon_add_query_string_to_domain', false, $image_url_parts['host'] ) ) {
$photon_url .= '?q=' . rawurlencode( $image_url_parts['query'] );
}
if ( $args ) {
if ( is_array( $args ) ) {
$photon_url = add_query_arg( $args, $photon_url );
} elseif ( strpos( $photon_url, '?' ) !== false ) {
$photon_url .= '&' . $args;
} else {
$photon_url .= '?' . $args;
}
}
if ( isset( $image_url_parts['scheme'] ) && 'https' === $image_url_parts['scheme'] ) {
$photon_url = add_query_arg( array( 'ssl' => 1 ), $photon_url );
}
return self::cdn_url_scheme( $photon_url, $scheme );
}
/**
* Checks if a given URL is a Photon URL.
*
* @since 0.5.0
* @param string $url The URL to check.
* @return bool True if the URL is a Photon URL, false otherwise.
*/
public static function is_cdn_url( $url ) {
$parsed_url = wp_parse_url( $url );
if ( ! $parsed_url ) {
return false;
}
// See usage in ::cdn_url for documentation of this filter
$custom_photon_url = apply_filters( 'jetpack_photon_domain', '', $url );
$custom_photon_url = esc_url( $custom_photon_url );
return in_array( $parsed_url['host'], array( 'i0.wp.com', 'i1.wp.com', 'i2.wp.com' ), true )
|| wp_parse_url( $custom_photon_url, PHP_URL_HOST ) === $parsed_url['host'];
}
/**
* URL-encodes each path component.
*
* Example:
* Input: "foo/bar baz/baz"
* Output: "foo/bar%20baz/baz"
*
* @param string $path The path to escape.
* @return string The escaped path.
*/
private static function escape_path( $path ) {
$parts = explode( '/', $path );
$parts = array_map( 'rawurldecode', $parts );
$parts = array_map( 'rawurlencode', $parts );
return implode( '/', $parts );
}
/**
* Parses WP.com-hosted image args to replicate the crop.
*
* @param mixed $args Args set during Photon's processing.
* @param string $image_url URL of the image.
* @return array|string Args for Photon to use for the URL.
*/
public static function parse_wpcom_query_args( $args, $image_url ) {
$parsed_url = wp_parse_url( $image_url );
if ( ! $parsed_url ) {
return $args;
}
$image_url_parts = wp_parse_args(
$parsed_url,
array(
'host' => '',
'query' => '',
)
);
if ( ! str_ends_with( $image_url_parts['host'], '.files.wordpress.com' ) ) {
return $args;
}
if ( empty( $image_url_parts['query'] ) ) {
return $args;
}
$wpcom_args = wp_parse_args( $image_url_parts['query'] );
if ( empty( $wpcom_args['w'] ) || empty( $wpcom_args['h'] ) ) {
return $args;
}
// Keep the crop by using "resize".
if ( ! empty( $wpcom_args['crop'] ) ) {
if ( is_array( $args ) ) {
$args = array_merge( array( 'resize' => array( $wpcom_args['w'], $wpcom_args['h'] ) ), $args );
} else {
$args = 'resize=' . rawurlencode( absint( $wpcom_args['w'] ) . ',' . absint( $wpcom_args['h'] ) ) . '&' . $args;
}
} elseif ( is_array( $args ) ) {
$args = array_merge( array( 'fit' => array( $wpcom_args['w'], $wpcom_args['h'] ) ), $args );
} else {
$args = 'fit=' . rawurlencode( absint( $wpcom_args['w'] ) . ',' . absint( $wpcom_args['h'] ) ) . '&' . $args;
}
return $args;
}
/**
* Sets the scheme for a URL
*
* @param string $url URL to set scheme.
* @param string $scheme Scheme to use. Accepts http, https, network_path.
*
* @return string URL.
*/
public static function cdn_url_scheme( $url, $scheme ) {
if ( ! in_array( $scheme, array( 'http', 'https', 'network_path' ), true ) ) {
if ( preg_match( '#^(https?:)?//#', $url ) ) {
return $url;
}
$scheme = 'http';
}
if ( 'network_path' === $scheme ) {
$scheme_slashes = '//';
} else {
$scheme_slashes = "$scheme://";
}
return preg_replace( '#^([a-z:]+)?//#i', $scheme_slashes, $url );
}
/**
* Check to skip Photon for a known domain that shouldn't be Photonized.
*
* @param bool $skip If the image should be skipped by Photon.
* @param string $image_url URL of the image.
*
* @return bool Should the image be skipped by Photon.
*/
public static function banned_domains( $skip, $image_url ) {
$banned_host_patterns = array(
'/^chart\.googleapis\.com$/',
'/^chart\.apis\.google\.com$/',
'/^graph\.facebook\.com$/',
'/\.fbcdn\.net$/',
'/\.paypalobjects\.com$/',
'/\.dropbox\.com$/',
'/\.cdninstagram\.com$/',
'/^(commons|upload)\.wikimedia\.org$/',
'/\.wikipedia\.org$/',
'/^m\.media-amazon\.com$/',
'/^covers\.openlibrary\.org$/',
);
$host = wp_parse_url( $image_url, PHP_URL_HOST );
if ( ! $host ) {
return $skip;
}
foreach ( $banned_host_patterns as $banned_host_pattern ) {
if ( 1 === preg_match( $banned_host_pattern, $host ) ) {
return true;
}
}
return $skip;
}
/**
* Check whether a string ends with a specific substring.
*
* @param string $haystack String we are filtering.
* @param string $needle The substring we are looking for.
* @return bool
*/
public static function ends_with( $haystack, $needle ) {
if ( ! $haystack || ! $needle || ! is_scalar( $haystack ) || ! is_scalar( $needle ) ) {
return false;
}
$haystack = (string) $haystack;
$needle = (string) $needle;
return str_ends_with( $haystack, $needle );
}
/**
* This is a copy of Jetpack::get_content_width()
* for backwards compatibility.
*/
public static function get_jetpack_content_width() {
$content_width = ( isset( $GLOBALS['content_width'] ) && is_numeric( $GLOBALS['content_width'] ) )
? $GLOBALS['content_width']
: false;
/**
* Filter the Content Width value.
*
* @since 2.2.3
*
* @param string $content_width Content Width value.
*/
return apply_filters( 'jetpack_content_width', $content_width );
}
}
@@ -0,0 +1,199 @@
<?php
/**
* The Image Sizes library.
*
* @package automattic/jetpack-image-cdn
*/
namespace Automattic\Jetpack\Image_CDN;
/**
* Class Image_CDN_Image_Sizes
*
* Manages image resizing via Jetpack CDN Service.
*/
class Image_CDN_Image_Sizes {
/**
* Attachment metadata.
*
* @var array
*/
public $data;
/**
* Image to be resized.
*
* @var Image_CDN_Image
*/
public $image;
/**
* Intermediate sizes.
*
* @var null|array
*/
public static $sizes = null;
/**
* Construct new sizes meta
*
* @param int $attachment_id Attachment ID.
* @param array $data Attachment metadata.
*/
public function __construct( $attachment_id, $data ) {
$this->data = $data;
$this->image = new Image_CDN_Image( $data, get_post_mime_type( $attachment_id ) );
$this->generate_sizes();
}
/**
* Generate sizes for attachment.
*
* @return array Array of sizes; empty array as failure fallback.
*/
protected function generate_sizes() {
// There is no need to generate the sizes anew for every single image.
if ( null !== self::$sizes ) {
return self::$sizes;
}
/*
* The following logic is copied over from wp_generate_attachment_metadata
*/
$_wp_additional_image_sizes = wp_get_additional_image_sizes();
$sizes = array();
$intermediate_image_sizes = get_intermediate_image_sizes();
foreach ( $intermediate_image_sizes as $s ) {
$sizes[ $s ] = array(
'width' => '',
'height' => '',
'crop' => false,
);
if ( isset( $_wp_additional_image_sizes[ $s ]['width'] ) ) {
// For theme-added sizes.
$sizes[ $s ]['width'] = (int) $_wp_additional_image_sizes[ $s ]['width'];
} else {
// For default sizes set in options.
$sizes[ $s ]['width'] = get_option( "{$s}_size_w" );
}
if ( isset( $_wp_additional_image_sizes[ $s ]['height'] ) ) {
// For theme-added sizes.
$sizes[ $s ]['height'] = (int) $_wp_additional_image_sizes[ $s ]['height'];
} else {
// For default sizes set in options.
$sizes[ $s ]['height'] = get_option( "{$s}_size_h" );
}
if ( isset( $_wp_additional_image_sizes[ $s ]['crop'] ) ) {
// For theme-added sizes.
$sizes[ $s ]['crop'] = $_wp_additional_image_sizes[ $s ]['crop'];
} else {
// For default sizes set in options.
$sizes[ $s ]['crop'] = get_option( "{$s}_crop" );
}
}
self::$sizes = $sizes;
return $sizes;
}
/**
* Add filtered sizes.
*
* @return array
*/
public function filtered_sizes() {
// Remove filter preventing the creation of advanced sizes.
remove_filter(
'intermediate_image_sizes_advanced',
array( Image_CDN::class, 'filter_photon_noresize_intermediate_sizes' )
);
// Compatibility fix: Ensure that Jetpack's inbuilt Photon does not get in the way.
// No need to re-enable this filter if present, as its functionality will be handled
// by the Image_CDN filter added below.
remove_filter(
'intermediate_image_sizes_advanced',
array( 'Jetpack_Photon', 'filter_photon_noresize_intermediate_sizes' )
);
/** This filter is documented in wp-admin/includes/image.php */
$sizes = apply_filters( 'intermediate_image_sizes_advanced', self::$sizes, $this->data );
// Re-add the filter removed above.
add_filter(
'intermediate_image_sizes_advanced',
array( Image_CDN::class, 'filter_photon_noresize_intermediate_sizes' )
);
return (array) $sizes;
}
/**
* Standardises and validates the size_data array.
*
* @param array $size_data Size data array - at least containing height or width key. Can contain crop as well.
*
* @return array Array with populated width, height and crop keys; empty array if no width and height are provided.
*/
public function standardize_size_data( $size_data ) {
$has_at_least_width_or_height = ( isset( $size_data['width'] ) || isset( $size_data['height'] ) );
if ( ! $has_at_least_width_or_height ) {
return array();
}
$defaults = array(
'width' => null,
'height' => null,
'crop' => false,
);
return array_merge( $defaults, $size_data );
}
/**
* Get sizes for attachment post meta.
*
* @return array ImageSizes for attachment postmeta.
*/
public function generate_sizes_meta() {
$metadata = array();
foreach ( $this->filtered_sizes() as $size => $size_data ) {
$size_data = $this->standardize_size_data( $size_data );
if ( empty( $size_data ) ) {
continue;
}
$resized_image = $this->resize( $size_data );
if ( is_array( $resized_image ) ) {
$metadata[ $size ] = $resized_image;
}
}
return $metadata;
}
/**
* Resize image.
*
* @param array $size_data Resize parameters.
*
* @return array|\WP_Error Array for usage in $metadata['sizes']; WP_Error on failure.
*/
protected function resize( $size_data ) {
return $this->image->get_size( $size_data );
}
}
@@ -0,0 +1,267 @@
<?php
/**
* The Image Class.
*
* @package automattic/jetpack-image-cdn
*/
namespace Automattic\Jetpack\Image_CDN;
use WP_Error;
/**
* Represents a resizable image, exposing properties necessary for properly generating srcset.
*/
class Image_CDN_Image {
/**
* Attachment's Filename.
*
* @var string
*/
public $filename;
/**
* Attachment's mime-type, WP_Error on failure when recalculating the dimensions.
*
* @var string|WP_Error
*/
private $mime_type;
/**
* Image original width.
*
* @var int
*/
private $original_width;
/**
* Image original height.
*
* @var int
*/
private $original_height;
/**
* Current attachment's width.
*
* @var int
*/
private $width;
/**
* Current attachment's height.
*
* @var int
*/
private $height;
/**
* Whether the attachment has been resized yet, or not.
*
* @var bool
*/
private $is_resized = false;
/**
* Constructs the image object.
*
* The $data array should provide at least
* file : string Image file path
* width : int Image width
* height : int Image height
*
* @param array $data Array of attachment metadata, typically value of _wp_attachment_metadata postmeta.
* @param string|WP_Error $mime_type Typically value returned from get_post_mime_type function.
*/
public function __construct( $data, $mime_type ) {
$this->filename = $data['file'];
$this->original_width = $data['width'];
$this->original_height = $data['height'];
$this->width = $this->original_width;
$this->height = $this->original_height;
$this->mime_type = $mime_type;
}
/**
* Resizes the image to given size.
*
* @param array $size_data Array of width, height, and crop properties of a size.
*
* @return bool|WP_Error True if resize was successful, WP_Error on failure.
*/
public function resize( $size_data ) {
$dimensions = $this->image_resize_dimensions( $size_data['width'], $size_data['height'], $size_data['crop'] );
if ( true === is_wp_error( $dimensions ) ) {
return $dimensions; // Returns WP_Error.
}
if ( true === is_wp_error( $this->mime_type ) ) {
return $this->mime_type; // Returns WP_Error.
}
$this->set_width_height( $dimensions );
$this->is_resized = true;
return true;
}
/**
* Generates size data for usage in $metadata['sizes'];.
*
* @param array $size_data Array of width, height, and crop properties of a size.
*
* @return array|WP_Error An array containing file, width, height, and mime-type keys and it's values. WP_Error on failure.
*/
public function get_size( $size_data ) {
$is_resized = $this->resize( $size_data );
if ( true === is_wp_error( $is_resized ) ) {
return $is_resized;
}
return array(
'file' => $this->get_filename(),
'width' => $this->get_width(),
'height' => $this->get_height(),
'mime-type' => $this->get_mime_type(),
);
}
/**
* Resets the image to it's original dimensions.
*
* @return bool True on successful reset to original dimensions.
*/
public function reset_to_original() {
$this->width = $this->original_width;
$this->height = $this->original_height;
$this->is_resized = false;
return true;
}
/**
* Return the basename filename. If the image has been resized, including
* the resizing params for Jetpack CDN.
*
* @return string Basename of the filename.
*/
public function get_filename() {
return wp_basename( $this->get_raw_filename() );
}
/**
* Return the absolute filename. If the image has been resized, including
* the resizing params for Jetpack CDN.
*
* @return string Filename.
*/
public function get_raw_filename() {
return $this->is_resized() ? $this->get_resized_filename() : $this->filename;
}
/**
* Returns current image width. Either original, or after resize.
*
* @return int
*/
public function get_width() {
return (int) $this->width;
}
/**
* Returns current image height. Either original, or after resize.
*
* @return int
*/
public function get_height() {
return (int) $this->height;
}
/**
* Returns image mime type.
*
* @return string|WP_Error Image's mime type or WP_Error if it was not determined.
*/
public function get_mime_type() {
return $this->mime_type;
}
/**
* Checks the resize status of the image.
*
* @return bool If the image has been resized.
*/
public function is_resized() {
return ( true === $this->is_resized );
}
/**
* Get filename with proper args for the Photon service.
*
* @return string Filename with query args for Photon service
*/
protected function get_resized_filename() {
$query_args = array(
'resize' => implode(
',',
array(
$this->get_width(),
$this->get_height(),
)
),
);
return add_query_arg( $query_args, $this->filename );
}
/**
* Get resize dimensions used for the Jetpack CDN service.
*
* Converts the list of values returned from `image_resize_dimensions()` to
* associative array for the sake of more readable code no relying on index
* nor `list`.
*
* @param int $max_width Maximum width.
* @param int $max_height Maximum height.
* @param bool|array $crop Cropping parameters.
*
* @return array|WP_Error Array of dimensions matching the parameters to imagecopyresampled. WP_Error on failure.
*/
protected function image_resize_dimensions( $max_width, $max_height, $crop ) {
$dimensions = image_resize_dimensions( $this->original_width, $this->original_height, $max_width, $max_height, $crop );
if ( ! $dimensions ) {
return new WP_Error( 'error_getting_dimensions', __( 'Could not calculate resized image dimensions', 'jetpack-image-cdn' ), $this->filename );
}
return array_combine(
array(
'dst_x',
'dst_y',
'src_x',
'src_y',
'dst_w',
'dst_h',
'src_w',
'src_h',
),
$dimensions
);
}
/**
* Sets proper width and height from dimensions.
*
* @param array $dimensions an array of image dimensions.
* @return void
*/
protected function set_width_height( $dimensions ) {
$this->width = (int) $dimensions['dst_w'];
$this->height = (int) $dimensions['dst_h'];
}
}
@@ -0,0 +1,28 @@
<?php
/**
* Package description here
*
* @package automattic/jetpack-image-cdn
*/
namespace Automattic\Jetpack\Image_CDN;
/**
* Class description.
*/
class Image_CDN_Setup {
/**
* Initialize Image CDN.
*/
public static function load() {
\Automattic\Jetpack\Assets::add_resource_hint(
array(
'//i0.wp.com',
),
'preconnect'
);
Image_CDN::instance();
}
}
@@ -0,0 +1,62 @@
<?php
/**
* Compatibility functions for the ActivityPub plugin.
*
* @since 0.2.2
*
* @package automattic/jetpack-image-cdn
*/
namespace Automattic\Jetpack\Image_CDN\Compatibility;
use Automattic\Jetpack\Image_CDN\Image_CDN;
if ( ! defined( 'ABSPATH' ) ) {
exit( 0 );
}
/**
* Hook the compatibility functions into ActivityPub filters if necessary.
*
* @since 0.2.2
*
* @return void
*/
function load_activitypub_compat() {
if (
/**
* Allow disabling Jetpack's image CDN for ActivityPub requests.
*
* @since 0.2.2
*
* @param bool $should_disable_photon Should the CDN be disabled for that request. Default to false.
*/
apply_filters( 'jetpack_activitypub_post_disable_cdn', false )
) {
add_action( 'activitypub_get_image_pre', __NAMESPACE__ . '\disable_photon' );
add_action( 'activitypub_get_image_post', __NAMESPACE__ . '\enable_photon' );
}
}
add_action( 'plugins_loaded', __NAMESPACE__ . '\load_activitypub_compat' );
/**
* Disable Jetpack's Image CDN processing for this request.
*
* @see https://github.com/pfefferle/wordpress-activitypub/pull/309
*
* @return void
*/
function disable_photon() {
remove_filter( 'image_downsize', array( Image_CDN::instance(), 'filter_image_downsize' ) );
}
/**
* Re-enable Jetpack's Image CDN processing after the request.
*
* @see https://github.com/pfefferle/wordpress-activitypub/pull/309
*
* @return void
*/
function enable_photon() {
add_filter( 'image_downsize', array( Image_CDN::instance(), 'filter_image_downsize' ), 10, 3 );
}
@@ -0,0 +1,42 @@
<?php
/**
* Compatibility functions for the Breakdance plugin.
*
* @since 0.7.20
*
* @package automattic/jetpack-image-cdn
*/
namespace Automattic\Jetpack\Image_CDN\Compatibility;
use Automattic\Jetpack\Image_CDN\Image_CDN;
/**
* Hook the compatibility functions into Breakdance filters.
*
* @since 0.7.20
*
* @return void
*/
function load_breakdance_compat() {
add_filter( 'breakdance_singular_content', __NAMESPACE__ . '\use_image_cdn' );
}
add_action( 'plugins_loaded', __NAMESPACE__ . '\load_breakdance_compat' );
/**
* Unless the 'Apply the_content filter to Breakdance content' option is enabled
* in the Breakdance settings, the content will not be filtered by the_content filter.
* This ensures that images are passed through Image CDN when it's enabled.
*
* @since 0.7.20
*
* @param string $content The content to filter.
* @return string The filtered content.
*/
function use_image_cdn( $content ) {
if ( Image_CDN::is_enabled() ) {
$content = Image_CDN::filter_the_content( $content );
}
return $content;
}
@@ -0,0 +1,53 @@
<?php
/**
* Compatibility for Photon
*
* Replace photon with image-cdn.
*
* @package automattic/jetpack-image-cdn
*/
namespace Automattic\Jetpack\Image_CDN\Compatibility;
if ( ! defined( 'ABSPATH' ) ) {
exit( 0 );
}
/**
* Disable the unpackaged photon version living in Jetpack.
*
* Before the package, Jetpack had the photon functionality within the plugin. To avoid double
* activation, we need to disable the photon functionality in Jetpack to avoid potential conflicts.
*
* If this package is installed via a different plugin, e.g. Boost, Image CDN should be served instead
* of the photon module in Jetpack.
*/
function jetpack_image_cdn_photon_compat() {
/*
* Photon used have different functions names. They are later replaced by methods in
* Image_CDN_Core class. And the filters are now handled by the Image_CDN_Core class itself.
*/
remove_filter( 'jetpack_photon_url', 'jetpack_photon_url', 10 );
remove_filter( 'jetpack_photon_pre_args', 'jetpack_photon_parse_wpcom_query_args', 10 );
remove_filter( 'jetpack_photon_skip_for_url', 'jetpack_photon_banned_domains', 9 );
remove_filter( 'widget_text', 'jetpack_photon_support_text_widgets' );
/*
* If photon is active in an old version of Jetpack which is using the unpackaged photon
* fake loading the module in Jetpack itself. This will prevent jetpack from loading
* the photon module, and we will use the image CDN package instead.
*
* When this is happening, the package is likely being loaded from a different plugin, e.g. Boost.
*/
if ( class_exists( 'Jetpack' ) && defined( 'JETPACK__VERSION' ) && ( version_compare( JETPACK__VERSION, '12.1', '<=' ) ) ) {
do_action( 'jetpack_module_loaded_photon' );
}
}
/*
* Remove the hooks that Jetpack uses to load photon and pretend that the module is already loaded.
*
* Jetpack hooks the filters as it loads. So, we can remove the filters when `plugins_loaded` fires.
* This would make sure Jetpack has already loaded and hooked the filters.
*/
add_action( 'plugins_loaded', __NAMESPACE__ . '\jetpack_image_cdn_photon_compat' );