You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
7.8 KiB
298 lines
7.8 KiB
<?php |
|
/** |
|
* Class AMP_Gallery_Block_Sanitizer. |
|
* |
|
* @package AMP |
|
*/ |
|
|
|
/** |
|
* Class AMP_Gallery_Block_Sanitizer |
|
* |
|
* Modifies gallery block to match the block's AMP-specific configuration. |
|
*/ |
|
class AMP_Gallery_Block_Sanitizer extends AMP_Base_Sanitizer { |
|
|
|
/** |
|
* Value used for width of amp-carousel. |
|
* |
|
* @since 1.0 |
|
* |
|
* @const int |
|
*/ |
|
const FALLBACK_WIDTH = 600; |
|
|
|
/** |
|
* Value used for height of amp-carousel. |
|
* |
|
* @since 1.0 |
|
* |
|
* @const int |
|
*/ |
|
const FALLBACK_HEIGHT = 480; |
|
|
|
/** |
|
* Tag. |
|
* |
|
* @since 1.0 |
|
* |
|
* @var string Ul tag to identify wrapper around gallery block. |
|
*/ |
|
public static $tag = 'ul'; |
|
|
|
/** |
|
* Expected class of the wrapper around the gallery block. |
|
* |
|
* @since 1.0 |
|
* |
|
* @var string |
|
*/ |
|
public static $class = 'wp-block-gallery'; |
|
|
|
/** |
|
* Array of flags used to control sanitization. |
|
* |
|
* @var array { |
|
* @type int $content_max_width Max width of content. |
|
* @type bool $carousel_required Whether carousels are required. This is used when amp theme support is not present, for back-compat. |
|
* } |
|
*/ |
|
protected $args; |
|
|
|
/** |
|
* Default args. |
|
* |
|
* @var array |
|
*/ |
|
protected $DEFAULT_ARGS = [ |
|
'carousel_required' => false, |
|
]; |
|
|
|
/** |
|
* Sanitize the gallery block contained by <ul> element where necessary. |
|
* |
|
* @since 0.2 |
|
*/ |
|
public function sanitize() { |
|
$xpath = new DOMXPath( $this->dom ); |
|
$class_query = 'contains( concat( " ", normalize-space( @class ), " " ), " wp-block-gallery " )'; |
|
$expr = sprintf( |
|
'//ul[ %s ]', |
|
implode( |
|
' or ', |
|
[ |
|
sprintf( '( parent::figure[ %s ] )', $class_query ), |
|
$class_query, |
|
] |
|
) |
|
); |
|
$query = $xpath->query( $expr ); |
|
|
|
$nodes = []; |
|
foreach ( $query as $node ) { |
|
$nodes[] = $node; |
|
} |
|
|
|
foreach ( $nodes as $node ) { |
|
/** |
|
* Element |
|
* |
|
* @var DOMElement $node |
|
*/ |
|
|
|
// In WordPress 5.3, the Gallery block's <ul> is wrapped in a <figure class="wp-block-gallery">, so look for that node also. |
|
$gallery_node = isset( $node->parentNode ) && AMP_DOM_Utils::has_class( $node->parentNode, self::$class ) ? $node->parentNode : $node; |
|
$attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $gallery_node ); |
|
|
|
$is_amp_lightbox = isset( $attributes['data-amp-lightbox'] ) && true === filter_var( $attributes['data-amp-lightbox'], FILTER_VALIDATE_BOOLEAN ); |
|
$is_amp_carousel = ( |
|
! empty( $this->args['carousel_required'] ) |
|
|| |
|
filter_var( $node->getAttribute( 'data-amp-carousel' ), FILTER_VALIDATE_BOOLEAN ) |
|
|| |
|
filter_var( $node->parentNode->getAttribute( 'data-amp-carousel' ), FILTER_VALIDATE_BOOLEAN ) |
|
); |
|
|
|
// We are only looking for <ul> elements which have amp-carousel / amp-lightbox true. |
|
if ( ! $is_amp_carousel && ! $is_amp_lightbox ) { |
|
continue; |
|
} |
|
|
|
// If lightbox is set, we should add lightbox feature to the gallery images. |
|
if ( $is_amp_lightbox ) { |
|
$this->add_lightbox_attributes_to_image_nodes( $node ); |
|
$this->maybe_add_amp_image_lightbox_node(); |
|
} |
|
|
|
// If amp-carousel is not set, nothing else to do here. |
|
if ( ! $is_amp_carousel ) { |
|
continue; |
|
} |
|
|
|
$images = []; |
|
|
|
// If it's not AMP lightbox, look for links first. |
|
if ( ! $is_amp_lightbox ) { |
|
foreach ( $node->getElementsByTagName( 'a' ) as $element ) { |
|
$images[] = $element; |
|
} |
|
} |
|
|
|
// If not linking to anything then look for <amp-img>. |
|
if ( empty( $images ) ) { |
|
foreach ( $node->getElementsByTagName( 'amp-img' ) as $element ) { |
|
$images[] = $element; |
|
} |
|
} |
|
|
|
// Skip if no images found. |
|
if ( empty( $images ) ) { |
|
continue; |
|
} |
|
|
|
list( $width, $height ) = $this->get_carousel_dimensions( $node ); |
|
|
|
$amp_carousel = AMP_DOM_Utils::create_node( |
|
$this->dom, |
|
'amp-carousel', |
|
[ |
|
'width' => $width, |
|
'height' => $height, |
|
'type' => 'slides', |
|
'layout' => 'responsive', |
|
] |
|
); |
|
|
|
foreach ( $images as $image ) { |
|
$slide = AMP_DOM_Utils::create_node( |
|
$this->dom, |
|
'div', |
|
[ 'class' => 'slide' ] |
|
); |
|
|
|
// Ensure the image fills the entire <amp-carousel>, so the possible caption looks right. |
|
if ( 'amp-img' === $image->tagName ) { |
|
$image->setAttribute( 'layout', 'fill' ); |
|
$image->setAttribute( 'object-fit', 'cover' ); |
|
} elseif ( isset( $image->firstChild->tagName ) && 'amp-img' === $image->firstChild->tagName ) { |
|
// If the <amp-img> is wrapped in an <a>. |
|
$image->firstChild->setAttribute( 'layout', 'fill' ); |
|
$image->firstChild->setAttribute( 'object-fit', 'cover' ); |
|
} |
|
|
|
$possible_caption_text = $this->possibly_get_caption_text( $image ); |
|
$slide->appendChild( $image ); |
|
|
|
// Wrap the caption in a <div> and <span>, and append it to the slide. |
|
if ( $possible_caption_text ) { |
|
$caption_wrapper = AMP_DOM_Utils::create_node( |
|
$this->dom, |
|
'div', |
|
[ 'class' => 'amp-wp-gallery-caption' ] |
|
); |
|
$caption_span = AMP_DOM_Utils::create_node( $this->dom, 'span', [] ); |
|
$text_node = $this->dom->createTextNode( $possible_caption_text ); |
|
|
|
$caption_span->appendChild( $text_node ); |
|
$caption_wrapper->appendChild( $caption_span ); |
|
$slide->appendChild( $caption_wrapper ); |
|
} |
|
|
|
$amp_carousel->appendChild( $slide ); |
|
} |
|
|
|
$gallery_node->parentNode->replaceChild( $amp_carousel, $gallery_node ); |
|
} |
|
$this->did_convert_elements = true; |
|
} |
|
|
|
/** |
|
* Get carousel height by containing images. |
|
* |
|
* @param DOMElement $element The UL element. |
|
* @return array { |
|
* Dimensions. |
|
* |
|
* @type int $width Width. |
|
* @type int $height Height. |
|
* } |
|
*/ |
|
protected function get_carousel_dimensions( $element ) { |
|
/** |
|
* Elements. |
|
* |
|
* @var DOMElement $image |
|
*/ |
|
$images = $element->getElementsByTagName( 'amp-img' ); |
|
$num_images = $images->length; |
|
|
|
$max_aspect_ratio = 0; |
|
$carousel_width = 0; |
|
$carousel_height = 0; |
|
|
|
if ( 0 === $num_images ) { |
|
return [ self::FALLBACK_WIDTH, self::FALLBACK_HEIGHT ]; |
|
} |
|
foreach ( $images as $image ) { |
|
if ( ! is_numeric( $image->getAttribute( 'width' ) ) || ! is_numeric( $image->getAttribute( 'height' ) ) ) { |
|
continue; |
|
} |
|
$width = (float) $image->getAttribute( 'width' ); |
|
$height = (float) $image->getAttribute( 'height' ); |
|
|
|
$this_aspect_ratio = $width / $height; |
|
if ( $this_aspect_ratio > $max_aspect_ratio ) { |
|
$max_aspect_ratio = $this_aspect_ratio; |
|
$carousel_width = $width; |
|
$carousel_height = $height; |
|
} |
|
} |
|
|
|
return [ $carousel_width, $carousel_height ]; |
|
} |
|
|
|
/** |
|
* Set lightbox related attributes to <amp-img> within gallery. |
|
* |
|
* @param DOMElement $element The UL element. |
|
*/ |
|
protected function add_lightbox_attributes_to_image_nodes( $element ) { |
|
$images = $element->getElementsByTagName( 'amp-img' ); |
|
$num_images = $images->length; |
|
if ( 0 === $num_images ) { |
|
return; |
|
} |
|
$attributes = [ |
|
'data-amp-lightbox' => '', |
|
'on' => 'tap:' . self::AMP_IMAGE_LIGHTBOX_ID, |
|
'role' => 'button', |
|
'tabindex' => 0, |
|
]; |
|
|
|
for ( $j = $num_images - 1; $j >= 0; $j-- ) { |
|
$image_node = $images->item( $j ); |
|
foreach ( $attributes as $att => $value ) { |
|
$image_node->setAttribute( $att, $value ); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Gets the caption of an image, if it exists. |
|
* |
|
* @param DOMElement $element The element for which to search for a caption. |
|
* @return string|null The caption for the image, or null. |
|
*/ |
|
public function possibly_get_caption_text( $element ) { |
|
$caption_tag = 'figcaption'; |
|
if ( isset( $element->nextSibling->nodeName ) && $caption_tag === $element->nextSibling->nodeName ) { |
|
return $element->nextSibling->textContent; |
|
} |
|
|
|
// If 'Link To' is selected, the image will be wrapped in an <a>, so search for the sibling of the <a>. |
|
if ( isset( $element->parentNode->nextSibling->nodeName ) && $caption_tag === $element->parentNode->nextSibling->nodeName ) { |
|
return $element->parentNode->nextSibling->textContent; |
|
} |
|
|
|
return null; |
|
} |
|
}
|
|
|