Skip to content

Commit

Permalink
Merge pull request #3285 from ampproject/add/gallery-caption
Browse files Browse the repository at this point in the history
Add caption support to the Gallery block in <amp-carousel> and fix displaying Gallery block as carousel in WP 5.3
  • Loading branch information
westonruter authored Oct 28, 2019
2 parents 0af5613 + 0f0fd39 commit 7cae80e
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 20 deletions.
16 changes: 16 additions & 0 deletions assets/css/src/amp-default.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,19 @@ body amp-audio:not([controls]) {
.amp-wp-default-form-message[submit-success] > p:empty {
display: none;
}

amp-carousel .amp-wp-gallery-caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
text-align: center;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
padding: 1rem;
}

.wp-block-gallery[data-amp-carousel="true"] {
display: block;
flex-wrap: unset;
}
104 changes: 90 additions & 14 deletions includes/sanitizers/class-amp-gallery-block-sanitizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,44 @@ class AMP_Gallery_Block_Sanitizer extends AMP_Base_Sanitizer {
* @since 0.2
*/
public function sanitize() {
$nodes = $this->dom->getElementsByTagName( self::$tag );
$num_nodes = $nodes->length;
if ( 0 === $num_nodes ) {
return;
$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;
}

for ( $i = $num_nodes - 1; $i >= 0; $i-- ) {
$node = $nodes->item( $i );
foreach ( $nodes as $node ) {
/**
* Element
*
* @var DOMElement $node
*/

// We're looking for <ul> elements that have at least one child and the proper class.
if ( 0 === count( $node->childNodes ) || false === strpos( $node->getAttribute( 'class' ), self::$class ) ) {
continue;
}
// 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 );

$attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $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'] ) || ( isset( $attributes['data-amp-carousel'] ) && true === filter_var( $attributes['data-amp-carousel'], 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 ) {
Expand Down Expand Up @@ -140,11 +161,46 @@ public function sanitize() {
'layout' => 'responsive',
]
);

foreach ( $images as $image ) {
$amp_carousel->appendChild( $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 );
}

$node->parentNode->replaceChild( $amp_carousel, $node );
$gallery_node->parentNode->replaceChild( $amp_carousel, $gallery_node );
}
$this->did_convert_elements = true;
}
Expand Down Expand Up @@ -219,4 +275,24 @@ protected function add_lightbox_attributes_to_image_nodes( $element ) {
}
}
}

/**
* 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;
}
}
109 changes: 103 additions & 6 deletions tests/php/test-class-amp-gallery-block-sanitizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,50 @@ public function get_data() {
'<ul data-amp-carousel="true"><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></a></figure></li></ul>',
],

'data_amp_with_carousel' => [
'data_amp_with_carousel_and_link' => [
'<ul class="wp-block-gallery" data-amp-carousel="true"><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></a></figure></li></ul>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></a></amp-carousel>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><div class="slide"><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400" layout="fill" object-fit="cover"></amp-img></a></div></amp-carousel>',
],

'data_amp_with_carousel_and_caption' => [
'<ul class="wp-block-gallery" data-amp-carousel="true"><li class="blocks-gallery-item"><figure><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img><figcaption>This is a caption</figcaption></figure></li></ul>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><div class="slide"><amp-img src="http://example.com/img.png" width="600" height="400" layout="fill" object-fit="cover"></amp-img><div class="amp-wp-gallery-caption"><span>This is a caption</span></div></div></amp-carousel>',
],

// WordPress 5.3 changed the markup for the Gallery block, wrapping it in a <figure>.
'data_amp_with_carousel_caption_5_3' => [
'<figure class="wp-block-gallery" data-amp-carousel="true"><ul><li class="blocks-gallery-item"><figure><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img><figcaption>This is a caption</figcaption></figure></li></ul></figure>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><div class="slide"><amp-img src="http://example.com/img.png" width="600" height="400" layout="fill" object-fit="cover"></amp-img><div class="amp-wp-gallery-caption"><span>This is a caption</span></div></div></amp-carousel>',
],

'data_amp_with_lightbox' => [
'<ul class="wp-block-gallery" data-amp-lightbox="true"><li class="blocks-gallery-item"><figure><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></figure></li></ul>',
'<ul class="wp-block-gallery" data-amp-lightbox="true"><li class="blocks-gallery-item"><figure><amp-img src="http://example.com/img.png" width="600" height="400" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0"></amp-img></figure></li></ul><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
],

'data_amp_with_lightbox_and_link' => [
'<ul class="wp-block-gallery" data-amp-lightbox="true"><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></a></figure></li></ul>',
'<ul class="wp-block-gallery" data-amp-lightbox="true"><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0"></amp-img></a></figure></li></ul><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
],

'data_amp_with_lightbox_5_3' => [
'<figure class="wp-block-gallery" data-amp-lightbox="true"><ul><li class="blocks-gallery-item"><figure><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></figure></li></ul></figure>',
'<figure class="wp-block-gallery" data-amp-lightbox="true"><ul><li class="blocks-gallery-item"><figure><amp-img src="http://example.com/img.png" width="600" height="400" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0"></amp-img></figure></li></ul></figure><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
],

'data_amp_with_lightbox_and_link_5_3' => [
'<figure class="wp-block-gallery" data-amp-lightbox="true"><ul><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></a></figure></li></ul></figure>',
'<figure class="wp-block-gallery" data-amp-lightbox="true"><ul><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0"></amp-img></a></figure></li></ul></figure><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
],

'data_amp_with_lightbox_and_carousel' => [
'<ul class="wp-block-gallery" data-amp-lightbox="true" data-amp-carousel="true"><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="1234" height="567"></amp-img></a></figure></li></ul>',
'<amp-carousel width="1234" height="567" type="slides" layout="responsive"><amp-img src="http://example.com/img.png" width="1234" height="567" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0"></amp-img></amp-carousel><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
'<amp-carousel width="1234" height="567" type="slides" layout="responsive"><div class="slide"><amp-img src="http://example.com/img.png" width="1234" height="567" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0" layout="fill" object-fit="cover"></amp-img></div></amp-carousel><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
],

'data_amp_with_lightbox_carousel_5_3' => [
'<figure class="wp-block-gallery" data-amp-lightbox="true" data-amp-carousel="true"><ul><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="1234" height="567"></amp-img></a></figure></li></ul></figure>',
'<amp-carousel width="1234" height="567" type="slides" layout="responsive"><div class="slide"><amp-img src="http://example.com/img.png" width="1234" height="567" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0" layout="fill" object-fit="cover"></amp-img></div></amp-carousel><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
],
];
}
Expand Down Expand Up @@ -91,14 +122,24 @@ public function get_reader_mode_data() {
'<ul data-amp-carousel="true"><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></a></figure></li></ul>',
],

'data_amp_with_carousel_and_caption' => [
'<ul class="wp-block-gallery" data-amp-carousel="true"><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></a><figcaption>Here is a caption</figcaption></figure></li></ul>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><div class="slide"><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400" layout="fill" object-fit="cover"></amp-img></a><div class="amp-wp-gallery-caption"><span>Here is a caption</span></div></div></amp-carousel>',
],

'data_amp_with_lightbox' => [
'<ul class="wp-block-gallery" data-amp-lightbox="true"><li class="blocks-gallery-item"><figure><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></figure></li></ul>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><div class="slide"><amp-img src="http://example.com/img.png" width="600" height="400" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0" layout="fill" object-fit="cover"></amp-img></div></amp-carousel><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
],

'data_amp_with_lightbox_and_link' => [
'<ul class="wp-block-gallery" data-amp-lightbox="true"><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></a></figure></li></ul>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><amp-img src="http://example.com/img.png" width="600" height="400" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0"></amp-img></amp-carousel><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><div class="slide"><amp-img src="http://example.com/img.png" width="600" height="400" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0" layout="fill" object-fit="cover"></amp-img></div></amp-carousel><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
],

'data_amp_with_lightbox_and_carousel' => [
'data_amp_lightbox_carousel_and_link' => [
'<ul class="wp-block-gallery" data-amp-lightbox="true" data-amp-carousel="true"><li class="blocks-gallery-item"><figure><a href="http://example.com"><amp-img src="http://example.com/img.png" width="600" height="400"></amp-img></a></figure></li></ul>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><amp-img src="http://example.com/img.png" width="600" height="400" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0"></amp-img></amp-carousel><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
'<amp-carousel width="600" height="400" type="slides" layout="responsive"><div class="slide"><amp-img src="http://example.com/img.png" width="600" height="400" data-amp-lightbox="" on="tap:amp-image-lightbox" role="button" tabindex="0" layout="fill" object-fit="cover"></amp-img></div></amp-carousel><amp-image-lightbox id="amp-image-lightbox" layout="nodisplay" data-close-button-aria-label="Close"></amp-image-lightbox>',
],
];
}
Expand All @@ -125,4 +166,60 @@ public function test_sanitizer_reader_mode( $source, $expected ) {
$content = preg_replace( '/(?<=>)\s+(?=<)/', '', $content );
$this->assertEquals( $expected, $content );
}

/**
* Gets the data for test_possibly_get_caption_text().
*
* @return array[] The source to use, the expected return value, and the tag type to pass as an argument.
*/
public function get_caption_text_data() {
return [
'no_amp_img_or_anchor' => [
'<div><img src="https://example.com/image.jpg"></div>',
null,
'img',
],
'amp_img_with_empty_caption' => [
'<amp-img src="https://example.com/image.jpg"></amp-img><figcaption></figcaption>',
null,
'amp-img',
],
'amp_img_with_caption' => [
'<amp-img src="https://example.com/image.jpg"></amp-img><figcaption>This is a caption</figcaption>',
'This is a caption',
'amp-img',
],
'amp_img_wrapped_in_anchor_with_caption_in_div' => [
'<a href="https://example.com"><amp-img src="https://example.com/image.jpg"></amp-img></a><div>This is a caption</div>',
null,
'a',
],
'amp_img_wrapped_in_anchor_with_caption_in_figcaption' => [
'<a href="https://example.com"><amp-img src="https://example.com/image.jpg"></amp-img></a><figcaption>This is a caption</figcaption>',
'This is a caption',
'a',
],
];
}

/**
* Test possibly_get_caption_text.
*
* @covers \AMP_Gallery_Block_Sanitizer::possibly_get_caption_text()
*
* @dataProvider get_caption_text_data
* @param string $source The markup source to test.
* @param string|null $expected The expected return value of the tested method.
* @param string $element_name The name of the element to pass to the tested method.
*/
public function test_possibly_get_caption_text( $source, $expected, $element_name ) {
$dom = AMP_DOM_Utils::get_dom_from_content( $source );
$element = $dom->getElementsByTagName( $element_name )->item( 0 );
$sanitizer = new AMP_Gallery_Block_Sanitizer(
$dom,
[ 'content_max_width' => 600 ]
);

$this->assertEquals( $expected, $sanitizer->possibly_get_caption_text( $element ) );
}
}

0 comments on commit 7cae80e

Please sign in to comment.