From 7a2ca29b98af41bf0d38f483c3da1ae0996fc025 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 26 May 2020 12:37:30 -0700 Subject: [PATCH 01/47] Rework how and whether galleries are rendered as carousels --- assets/src/block-editor/helpers/index.js | 216 ++---------------- assets/src/block-editor/index.js | 3 +- .../embeds/class-amp-core-block-handler.php | 20 ++ .../class-amp-gallery-embed-handler.php | 21 +- .../class-amp-gallery-block-sanitizer.php | 16 +- 5 files changed, 59 insertions(+), 217 deletions(-) diff --git a/assets/src/block-editor/helpers/index.js b/assets/src/block-editor/helpers/index.js index a0f7be968c4..afd1996d133 100644 --- a/assets/src/block-editor/helpers/index.js +++ b/assets/src/block-editor/helpers/index.js @@ -8,7 +8,7 @@ import { ReactElement } from 'react'; * WordPress dependencies */ import { __, _x } from '@wordpress/i18n'; -import { cloneElement, RawHTML, render } from '@wordpress/element'; +import { cloneElement, render } from '@wordpress/element'; import { TextControl, SelectControl, ToggleControl, Notice, PanelBody, FontSizePicker } from '@wordpress/components'; import { InspectorControls } from '@wordpress/block-editor'; import { select } from '@wordpress/data'; @@ -94,15 +94,17 @@ const ampLayoutOptions = [ */ export const addAMPAttributes = ( settings, name ) => { // AMP Carousel settings. - if ( 'core/shortcode' === name || 'core/gallery' === name ) { + if ( 'core/gallery' === name ) { if ( ! settings.attributes ) { settings.attributes = {}; } settings.attributes.ampCarousel = { type: 'boolean', + default: ! select( 'amp/block-editor' ).hasThemeSupport(), // @todo We could just default this to false now even in Reader mode since block styles are loaded. }; settings.attributes.ampLightbox = { type: 'boolean', + default: false, }; } @@ -113,6 +115,7 @@ export const addAMPAttributes = ( settings, name ) => { } settings.attributes.ampLightbox = { type: 'boolean', + default: false, }; } @@ -124,6 +127,7 @@ export const addAMPAttributes = ( settings, name ) => { settings.attributes = {}; } settings.attributes.ampFitText = { + type: 'boolean', default: false, }; settings.attributes.minFont = { @@ -173,58 +177,12 @@ export const addAMPAttributes = ( settings, name ) => { * @return {Object} Output element. */ export const filterBlocksSave = ( element, blockType, attributes ) => { // eslint-disable-line complexity - let text = attributes.text || '', - content = ''; - const fitTextProps = { layout: 'fixed-height', }; - if ( 'core/shortcode' === blockType.name && isGalleryShortcode( attributes ) ) { - if ( ! attributes.ampLightbox ) { - if ( hasGalleryShortcodeLightboxAttribute( attributes.text || '' ) ) { - text = removeAmpLightboxFromShortcodeAtts( attributes.text ); - } - } - if ( attributes.ampCarousel ) { - // If the text contains amp-carousel or amp-lightbox, lets remove it. - if ( hasGalleryShortcodeCarouselAttribute( text ) ) { - text = removeAmpCarouselFromShortcodeAtts( text ); - } - - // If lightbox is not set, we can return here. - if ( ! attributes.ampLightbox ) { - if ( attributes.text !== text ) { - return ( - - { text } - - ); - } - - // Else lets return original. - return element; - } - } else if ( ! hasGalleryShortcodeCarouselAttribute( attributes.text || '' ) ) { - // Add amp-carousel=false attribute to the shortcode. - text = attributes.text.replace( '[gallery', '[gallery amp-carousel=false' ); - } else { - text = attributes.text; - } - - if ( attributes.ampLightbox && ! hasGalleryShortcodeLightboxAttribute( text ) ) { - text = text.replace( '[gallery', '[gallery amp-lightbox=true' ); - } - - if ( attributes.text !== text ) { - return ( - - { text } - - ); - } - } else if ( 'core/paragraph' === blockType.name && ! attributes.ampFitText ) { - content = getAmpFitTextContent( attributes.content ); + if ( 'core/paragraph' === blockType.name && ! attributes.ampFitText ) { + const content = getAmpFitTextContent( attributes.content ); if ( content !== attributes.content ) { return cloneElement( element, @@ -302,48 +260,6 @@ export const getLayoutOptions = ( block ) => { return layoutOptions; }; -/** - * Add extra data-amp-layout attribute to save to DB. - * - * @param {Object} props Properties. - * @param {Object} blockType Block type. - * @param {Object} blockType.name Block type name. - * @param {Object} attributes Attributes. - * - * @return {Object} Props. - */ -export const addAMPExtraProps = ( props, blockType, attributes ) => { - const ampAttributes = {}; - - // Shortcode props are handled differently. - if ( 'core/shortcode' === blockType.name ) { - return props; - } - - // AMP blocks handle layout and other props on their own. - if ( 'amp/' === blockType.name.substr( 0, 4 ) ) { - return props; - } - - if ( attributes.ampLayout ) { - ampAttributes[ 'data-amp-layout' ] = attributes.ampLayout; - } - if ( attributes.ampNoLoading ) { - ampAttributes[ 'data-amp-noloading' ] = attributes.ampNoLoading; - } - if ( attributes.ampLightbox ) { - ampAttributes[ 'data-amp-lightbox' ] = attributes.ampLightbox; - } - if ( attributes.ampCarousel ) { - ampAttributes[ 'data-amp-carousel' ] = attributes.ampCarousel; - } - - return { - ...ampAttributes, - ...props, - }; -}; - /** * Filters blocks edit function of all blocks. * @@ -353,26 +269,11 @@ export const addAMPExtraProps = ( props, blockType, attributes ) => { */ export const filterBlocksEdit = ( BlockEdit ) => { const EnhancedBlockEdit = function( props ) { - const { attributes: { text, ampLayout }, setAttributes, name } = props; + const { attributes: { ampLayout }, name } = props; let inspectorControls; - if ( 'core/shortcode' === name ) { - // Lets remove amp-carousel from edit view. - if ( hasGalleryShortcodeCarouselAttribute( text || '' ) ) { - setAttributes( { text: removeAmpCarouselFromShortcodeAtts( text ) } ); - } - // Lets remove amp-lightbox from edit view. - if ( hasGalleryShortcodeLightboxAttribute( text || '' ) ) { - setAttributes( { text: removeAmpLightboxFromShortcodeAtts( text ) } ); - } - - inspectorControls = setUpShortcodeInspectorControls( props ); - if ( '' === inspectorControls ) { - // Return original. - return ; - } - } else if ( 'core/gallery' === name ) { + if ( 'core/gallery' === name ) { inspectorControls = setUpGalleryInspectorControls( props ); } else if ( 'core/image' === name ) { inspectorControls = setUpImageInspectorControls( props ); @@ -666,7 +567,7 @@ const setUpTextBlocksInspectorControls = ( props ) => { setUpTextBlocksInspectorControls.propTypes = { isSelected: PropTypes.bool, attributes: PropTypes.shape( { - ampFitText: PropTypes.string, + ampFitText: PropTypes.bool, minFont: PropTypes.number, maxFont: PropTypes.number, height: PropTypes.number, @@ -674,38 +575,6 @@ setUpTextBlocksInspectorControls.propTypes = { setAttributes: PropTypes.func.isRequired, }; -/** - * Set up inspector controls for shortcode block. - * Adds ampCarousel attribute in case of gallery shortcode. - * - * @param {Object} props Props. - * - * @return {ReactElement} Inspector controls. - */ -const setUpShortcodeInspectorControls = ( props ) => { - const { isSelected } = props; - - if ( ! isGalleryShortcode( props.attributes ) || ! isSelected ) { - return null; - } - - const hasThemeSupport = select( 'amp/block-editor' ).hasThemeSupport(); - - return ( - - - { hasThemeSupport && } - - - - ); -}; - -setUpShortcodeInspectorControls.propTypes = { - isSelected: PropTypes.bool, - attributes: PropTypes.object, -}; - /** * Get AMP Lightbox toggle control. * @@ -739,7 +608,7 @@ const AmpLightboxToggle = ( props ) => { AmpLightboxToggle.propTypes = { attributes: PropTypes.shape( { - ampLightbox: PropTypes.string, + ampLightbox: PropTypes.bool, ampLayout: PropTypes.string, linkTo: PropTypes.string, } ), @@ -770,7 +639,7 @@ const AmpCarouselToggle = ( props ) => { AmpCarouselToggle.propTypes = { attributes: PropTypes.shape( { - ampCarousel: PropTypes.string, + ampCarousel: PropTypes.bool, } ), setAttributes: PropTypes.func.isRequired, }; @@ -821,12 +690,10 @@ const setUpGalleryInspectorControls = ( props ) => { return null; } - const hasThemeSupport = select( 'amp/block-editor' ).hasThemeSupport(); - return ( - { hasThemeSupport && } + @@ -837,61 +704,6 @@ setUpGalleryInspectorControls.propTypes = { isSelected: PropTypes.bool, }; -/** - * Removes amp-carousel=false from shortcode attributes. - * - * @param {string} shortcode Shortcode text. - * - * @return {string} Modified shortcode. - */ -export const removeAmpCarouselFromShortcodeAtts = ( shortcode ) => { - return shortcode.replace( ' amp-carousel=false', '' ); -}; - -/** - * Removes amp-lightbox=true from shortcode attributes. - * - * @param {string} shortcode Shortcode text. - * - * @return {string} Modified shortcode. - */ -export const removeAmpLightboxFromShortcodeAtts = ( shortcode ) => { - return shortcode.replace( ' amp-lightbox=true', '' ); -}; - -/** - * Determines whether a shortcode includes the amp-carousel attribute. - * - * @param {string} text Shortcode. - * - * @return {boolean} Whether the shortcode includes the attribute. - */ -export const hasGalleryShortcodeCarouselAttribute = ( text ) => { - return -1 !== text.indexOf( 'amp-carousel=false' ); -}; - -/** - * Determines whether a shortcode includes the amp-lightbox attribute. - * - * @param {string} text Shortcode. - * - * @return {boolean} Whether the shortcode includes the attribute. - */ -export const hasGalleryShortcodeLightboxAttribute = ( text ) => { - return -1 !== text.indexOf( 'amp-lightbox=true' ); -}; - -/** - * Determines whether the current shortcode is a gallery shortcode. - * - * @param {Object} attributes Shortcode attributes. - * - * @return {boolean} Whether it is a gallery shortcode. - */ -export const isGalleryShortcode = ( attributes ) => { - return attributes.text && -1 !== attributes.text.indexOf( 'gallery' ); -}; - /** * Determines whether AMP is enabled for the current post or not. * diff --git a/assets/src/block-editor/index.js b/assets/src/block-editor/index.js index 88d4a197e7b..f625e6f2fdd 100644 --- a/assets/src/block-editor/index.js +++ b/assets/src/block-editor/index.js @@ -13,7 +13,7 @@ import domReady from '@wordpress/dom-ready'; import { withFeaturedImageNotice } from '../common/components'; import { getMinimumFeaturedImageDimensions } from '../common/helpers'; import { withMediaLibraryNotice, AMPPreview } from './components'; -import { addAMPAttributes, addAMPExtraProps, filterBlocksEdit, filterBlocksSave, renderPreviewButton } from './helpers'; +import { addAMPAttributes, filterBlocksEdit, filterBlocksSave, renderPreviewButton } from './helpers'; import './store'; const { @@ -31,7 +31,6 @@ plugins.keys().forEach( ( modulePath ) => { addFilter( 'blocks.registerBlockType', 'ampEditorBlocks/addAttributes', addAMPAttributes ); addFilter( 'blocks.getSaveElement', 'ampEditorBlocks/filterSave', filterBlocksSave ); addFilter( 'editor.BlockEdit', 'ampEditorBlocks/filterEdit', filterBlocksEdit, 20 ); -addFilter( 'blocks.getSaveContent.extraProps', 'ampEditorBlocks/addExtraAttributes', addAMPExtraProps ); addFilter( 'editor.PostFeaturedImage', 'ampEditorBlocks/withFeaturedImageNotice', withFeaturedImageNotice ); addFilter( 'editor.MediaUpload', 'ampEditorBlocks/withMediaLibraryNotice', ( InitialMediaUpload ) => withMediaLibraryNotice( InitialMediaUpload, getMinimumFeaturedImageDimensions() ) ); diff --git a/includes/embeds/class-amp-core-block-handler.php b/includes/embeds/class-amp-core-block-handler.php index 94de522b13a..a656d03545a 100644 --- a/includes/embeds/class-amp-core-block-handler.php +++ b/includes/embeds/class-amp-core-block-handler.php @@ -76,6 +76,26 @@ public function unregister_embed() { * @return string Filtered block content. */ public function filter_rendered_block( $block_content, $block ) { + + if ( isset( $block['attrs'] ) ) { + $injected_attributes = ''; + $prop_attribute_mapping = [ + 'ampLayout' => 'data-amp-layout', + 'ampNoLoading' => 'data-amp-noloading', + 'ampLightbox' => 'data-amp-lightbox', + 'ampCarousel' => 'data-amp-carousel', + ]; + foreach ( $prop_attribute_mapping as $prop => $attr ) { + if ( isset( $block['attrs'][ $prop ] ) ) { + $property_value = rest_sanitize_boolean( $block['attrs'][ $prop ] ); + $injected_attributes .= sprintf( ' %s="%s"', $attr, esc_attr( wp_json_encode( $property_value ) ) ); + } + } + if ( $injected_attributes ) { + $block_content = preg_replace( '/(<\w+)/', '$1' . $injected_attributes, $block_content, 1 ); + } + } + if ( ! isset( $block['blockName'] ) ) { return $block_content; } diff --git a/includes/embeds/class-amp-gallery-embed-handler.php b/includes/embeds/class-amp-gallery-embed-handler.php index 875ad3e793a..d49a30b06e6 100644 --- a/includes/embeds/class-amp-gallery-embed-handler.php +++ b/includes/embeds/class-amp-gallery-embed-handler.php @@ -27,7 +27,10 @@ public function register_embed() { /** * Unregister embed. */ - public function unregister_embed() {} + public function unregister_embed() { + remove_filter( 'post_gallery', [ $this, 'maybe_override_gallery' ], 10 ); + remove_action( 'wp_print_styles', [ $this, 'print_styles' ] ); + } /** * Shortcode handler. @@ -61,7 +64,7 @@ public function shortcode( $attr ) { ); if ( ! empty( $attr['amp-lightbox'] ) ) { - $atts['lightbox'] = filter_var( $attr['amp-lightbox'], FILTER_VALIDATE_BOOLEAN ); + $atts['lightbox'] = rest_sanitize_boolean( $attr['amp-lightbox'] ); } $id = (int) $atts['id']; @@ -156,9 +159,17 @@ public function shortcode( $attr ) { * @return string $html Markup for the gallery. */ public function maybe_override_gallery( $html, $attributes ) { - $is_lightbox = isset( $attributes['amp-lightbox'] ) && true === filter_var( $attributes['amp-lightbox'], FILTER_VALIDATE_BOOLEAN ); - if ( isset( $attributes['amp-carousel'] ) && false === filter_var( $attributes['amp-carousel'], FILTER_VALIDATE_BOOLEAN ) ) { - if ( true === $is_lightbox ) { + $is_lightbox = isset( $attributes['amp-lightbox'] ) && rest_sanitize_boolean( $attributes['amp-lightbox'] ); + + // Use for the gallery if requested via amp-carousel shortcode attribute, or use by default if in Reader mode. + $is_carousel = ( + isset( $attributes['amp-carousel'] ) ? + rest_sanitize_boolean( $attributes['amp-carousel'] ) : + ! current_theme_supports( 'amp' ) // In AMP_Gallery_Block_Sanitizer, this is referred to as carousel_required. + ); + + if ( ! $is_carousel ) { + if ( $is_lightbox ) { $add_lightbox_attribute = static function ( $attr ) { $attr['lightbox'] = ''; return $attr; diff --git a/includes/sanitizers/class-amp-gallery-block-sanitizer.php b/includes/sanitizers/class-amp-gallery-block-sanitizer.php index cc66bf4745d..bce5d4d454f 100644 --- a/includes/sanitizers/class-amp-gallery-block-sanitizer.php +++ b/includes/sanitizers/class-amp-gallery-block-sanitizer.php @@ -81,14 +81,14 @@ public function sanitize() { $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 ) - ); + $is_amp_lightbox = isset( $attributes['data-amp-lightbox'] ) && rest_sanitize_boolean( $attributes['data-amp-lightbox'] ); + if ( $node->hasAttribute( 'data-amp-carousel' ) || $node->parentNode->hasAttribute( 'data-amp-carousel' ) ) { + $is_amp_carousel = rest_sanitize_boolean( $node->getAttribute( 'data-amp-carousel' ) ) || rest_sanitize_boolean( $node->parentNode->getAttribute( 'data-amp-carousel' ) ); + } else { + // The carousel_required argument is set to true when the theme does not support AMP. However, it is no + // no longer strictly required. Rather, carousels are just enabled by default. + $is_amp_carousel = ! empty( $this->args['carousel_required'] ); + } // We are only looking for