From ca1c9d994e9cc0f5daf2ed40fa20acd2b169e19d Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Fri, 10 Jul 2026 12:52:24 -0700 Subject: [PATCH] Tiled Gallery: add jetpack_skip_photon_domain filter to keep origin image URLs --- .../add-jetpack-skip-photon-domain-filter | 4 ++ .../jetpack/class.jetpack-gutenberg.php | 2 + .../blocks/tiled-gallery/tiled-gallery.php | 55 ++++++++++++++++++- .../blocks/tiled-gallery/utils/index.js | 24 +++++++- .../modules/tiled-gallery/tiled-gallery.php | 8 ++- .../tiled-gallery/tiled-gallery-item.php | 30 +++++++++- 6 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/add-jetpack-skip-photon-domain-filter diff --git a/projects/plugins/jetpack/changelog/add-jetpack-skip-photon-domain-filter b/projects/plugins/jetpack/changelog/add-jetpack-skip-photon-domain-filter new file mode 100644 index 000000000000..5abbe7403352 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-jetpack-skip-photon-domain-filter @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Tiled Gallery: add a `jetpack_skip_photon_domain` filter to serve images from the origin host (keeping the resizing query args) instead of routing them through the Photon domain, for platforms with a built-in image service such as WordPress VIP. diff --git a/projects/plugins/jetpack/class.jetpack-gutenberg.php b/projects/plugins/jetpack/class.jetpack-gutenberg.php index 3fa75194bea9..84588032e779 100644 --- a/projects/plugins/jetpack/class.jetpack-gutenberg.php +++ b/projects/plugins/jetpack/class.jetpack-gutenberg.php @@ -881,6 +881,8 @@ public static function enqueue_block_editor_assets() { 'jetpack_plan' => array( 'data' => $jetpack_plan['product_slug'], ), + /** This filter is documented in modules/tiled-gallery/tiled-gallery/tiled-gallery-item.php */ + 'skip_photon_domain' => (bool) apply_filters( 'jetpack_skip_photon_domain', false ), /** * Enable the RePublicize UI in the block editor context. * diff --git a/projects/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php b/projects/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php index 850d417a4958..60ddc5cdc7da 100644 --- a/projects/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php +++ b/projects/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php @@ -68,6 +68,11 @@ public static function render( $attr, $content ) { $jetpack_plan = Jetpack_Plan::get(); wp_localize_script( 'jetpack-gallery-settings', 'jetpack_plan', array( 'data' => $jetpack_plan['product_slug'] ) ); + /** This filter is documented in modules/tiled-gallery/tiled-gallery/tiled-gallery-item.php */ + $skip_photon_domain = (bool) apply_filters( 'jetpack_skip_photon_domain', false ); + // Expose the opt-out flag to the block's save-time JS (see photonizedImgProps() in utils/index.js). + wp_localize_script( 'jetpack-gallery-settings', 'jetpack_tiled_gallery_settings', array( 'skip_photon_domain' => $skip_photon_domain ) ); + if ( preg_match_all( '/]+>/', $content, $images ) ) { /** * This block processes all of the images that are found and builds $find and $replace. @@ -103,6 +108,13 @@ public static function render( $attr, $content ) { continue; } + // When the site opts out of the Photon domain, rewrite any already-baked-in + // i0.wp.com URLs back to their origin host so the srcset (and main src, below) + // serve from origin. + if ( $skip_photon_domain ) { + $orig_src = self::dephotonize_url( $orig_src, $is_ssl ); + } + $srcset_parts = array(); if ( $is_squareish_layout ) { $min_width = min( self::IMG_SRCSET_WIDTH_MIN, $orig_width, $orig_height ); @@ -116,7 +128,8 @@ public static function render( $attr, $content ) { ), $orig_src ); - if ( $is_ssl ) { + // `ssl` is a Photon-only signal; skip it on origin URLs when opting out of the Photon domain. + if ( $is_ssl && ! $skip_photon_domain ) { $srcset_src = add_query_arg( 'ssl', '1', $srcset_src ); } $srcset_parts[] = esc_url( $srcset_src ) . ' ' . $w . 'w'; @@ -136,7 +149,8 @@ public static function render( $attr, $content ) { ), $orig_src ); - if ( $is_ssl ) { + // `ssl` is a Photon-only signal; skip it on origin URLs when opting out of the Photon domain. + if ( $is_ssl && ! $skip_photon_domain ) { $srcset_src = add_query_arg( 'ssl', '1', $srcset_src ); } $srcset_parts[] = esc_url( $srcset_src ) . ' ' . $w . 'w'; @@ -151,8 +165,20 @@ public static function render( $attr, $content ) { if ( ! empty( $srcset_parts ) ) { $srcset = 'srcset="' . esc_attr( implode( ',', $srcset_parts ) ) . '"'; + $replacement = str_replace( '/?. This restores + * the origin URL (:///?) so that, when a site opts out of + * the Photon domain via the `jetpack_skip_photon_domain` filter, tiled gallery images are served + * from the origin host while keeping their resizing query args. The Photon-specific `ssl` + * argument is dropped. URLs that are not Photon URLs are returned unchanged. + * + * @param string $url The (possibly Photon) image URL. + * @param bool $is_ssl Whether the origin URL should use https (detected from the Photon ssl arg). + * @return string The origin URL, or the original URL if it was not a Photon URL. + */ + private static function dephotonize_url( $url, $is_ssl = true ) { + if ( ! preg_match( '#^(?:https?:)?//i[0-2]\.wp\.com/(.+)$#', $url, $matches ) ) { + return $url; + } + + $origin = ( $is_ssl ? 'https://' : 'http://' ) . $matches[1]; + + return remove_query_arg( 'ssl', $origin ); + } + /** * Render tiled gallery block for email. * diff --git a/projects/plugins/jetpack/extensions/blocks/tiled-gallery/utils/index.js b/projects/plugins/jetpack/extensions/blocks/tiled-gallery/utils/index.js index df914545e390..3bbea1ef5e5f 100644 --- a/projects/plugins/jetpack/extensions/blocks/tiled-gallery/utils/index.js +++ b/projects/plugins/jetpack/extensions/blocks/tiled-gallery/utils/index.js @@ -59,7 +59,10 @@ export function photonizedImgProps( img, galleryAtts = {} ) { const { height, width } = img; const { layoutStyle } = galleryAtts; - const photonImplementation = true === isVIP() || isSimpleSite() ? photonWpcomImage : photon; + // When a site opts out of the Photon domain (e.g. VIP with a platform image service), keep the + // origin host and just append the resizing query args — the behavior `photonWpcomImage` provides. + const photonImplementation = + skipPhotonDomain() || true === isVIP() || isSimpleSite() ? photonWpcomImage : photon; /** * Build the `src` @@ -129,6 +132,25 @@ function isVIP() { return jetpackPlan && jetpackPlan?.data === 'vip'; } +/** + * Whether the site has opted out of routing tiled gallery images through the Photon domain via the + * `jetpack_skip_photon_domain` PHP filter. The value is exposed to the editor through the Jetpack + * block editor initial state, with a localized `jetpack_tiled_gallery_settings` fallback (both set + * in tiled-gallery.php / class.jetpack-gutenberg.php). + * + * @return {boolean} True if the Photon domain should be skipped in favor of origin URLs. + */ +function skipPhotonDomain() { + /*global jetpack_tiled_gallery_settings*/ + if ( typeof window?.Jetpack_Editor_Initial_State?.jetpack?.skip_photon_domain !== 'undefined' ) { + return !! window.Jetpack_Editor_Initial_State.jetpack.skip_photon_domain; + } + if ( typeof jetpack_tiled_gallery_settings !== 'undefined' ) { + return !! jetpack_tiled_gallery_settings?.skip_photon_domain; + } + return false; +} + /** * Apply photon arguments to *.files.wordpress.com images * or images on mapped domains on private simple sites. diff --git a/projects/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php b/projects/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php index 15485fdb88d7..29cd3ade03ab 100644 --- a/projects/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php +++ b/projects/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php @@ -241,8 +241,12 @@ public function gallery_shortcode( $val, $atts ) { if ( $gallery_html && class_exists( 'Jetpack' ) && class_exists( Image_CDN::class ) ) { // Tiled Galleries in Jetpack require that Photon be active. - // If it's not active, run it just on the gallery output. - if ( ! Image_CDN::is_enabled() && ! ( new Status() )->is_offline_mode() ) { + // If it's not active, run it just on the gallery output — unless the site has + // opted out of Photon for tiled galleries (e.g. VIP with a built-in image service), + // in which case we must not rewrite the origin URLs back onto i0.wp.com. + /** This filter is documented in modules/tiled-gallery/tiled-gallery/tiled-gallery-item.php */ + if ( ! apply_filters( 'jetpack_skip_photon_domain', false ) + && ! Image_CDN::is_enabled() && ! ( new Status() )->is_offline_mode() ) { $gallery_html = Image_CDN::filter_the_content( $gallery_html ); } } diff --git a/projects/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery-item.php b/projects/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery-item.php index ac8c0d953711..98361bfc2eb4 100644 --- a/projects/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery-item.php +++ b/projects/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery-item.php @@ -110,9 +110,33 @@ public function __construct( $attachment_image, $needs_attachment_link, $graysca if ( $this->image->height === $this->image->width ) { $img_args['crop'] = true; } - // The function will always photonoize the URL (even if Photon is - // not active). We need to photonize the URL to set the width/height. - $this->img_src = Image_CDN_Core::cdn_url( $this->orig_file, $img_args ); + /** + * Allow sites to opt out of routing Tiled Gallery images through the Photon domain (the + * Jetpack Image CDN / i0.wp.com), while still appending the same resizing query args the + * tiled layout needs (w, h, crop, resize, strip). + * + * This is primarily intended for platforms such as WordPress VIP where a Photon-equivalent + * image service is built in and already understands those query args on the origin host, so + * rewriting URLs onto i0.wp.com is redundant or undesirable. + * + * When this returns true, image URLs keep their original domain and the args are simply + * added as query parameters instead of being sent through Image_CDN_Core::cdn_url(). + * + * @module tiled-gallery + * + * @since $$next-version$$ + * + * @param bool false Whether to skip the Photon domain and keep origin image URLs. Default false. + */ + if ( apply_filters( 'jetpack_skip_photon_domain', false ) ) { + // Keep the origin URL, but append the same query args Photon would have used so a + // platform-level image service (e.g. VIP) can resize/crop from the same parameters. + $this->img_src = add_query_arg( $img_args, $this->orig_file ); + } else { + // The function will always photonoize the URL (even if Photon is + // not active). We need to photonize the URL to set the width/height. + $this->img_src = Image_CDN_Core::cdn_url( $this->orig_file, $img_args ); + } $image_meta = wp_get_attachment_metadata( $attachment_image->ID ); $size_array = array( absint( $this->image->width ), absint( $this->image->height ) );