WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.52k stars 4.21k forks source link

Filterable behavior for Image Block raw transformations #8473

Open danielbachhuber opened 6 years ago

danielbachhuber commented 6 years ago

One of the plugins I work on, Tasty Pins, offers the ability to add "Pinterest Text" to an image (related #8472). When the image ends up in the Classic Editor, its markup looks like this:

image

Notice the inclusion of data-pin-description="This cashew coffee is delicious, creamy, and easy. Perfect for a chilly morning! | pinchof yum.com".

When this image is transformed to an Image Block, the data-pin-description attribute is lost, even though I've registered it as a custom attribute to the block (related #8470, #6878).

pinterestconversion

What's the best way to filter the Image Block transform behavior? It doesn't appear plugins have much control over the behavior:

https://github.com/WordPress/gutenberg/blob/804abecb7fee1acfbc764a9d6a1028ecf55b72c3/core-blocks/image/index.js#L112-L125

designsimply commented 6 years ago

Noting I deleted a comment which was saying the same thing over and over many times per minute in various locations. Spamming and unacceptable behavior towards anyone, or this community, will not be allowed. If you want to give feedback, please do it without spamming or personal attacks.

My apologies for those who were effected during this. Please know we have a zero tolerance policy over this type of behavior.

danielbachhuber commented 6 years ago

I made some progress on an implementation:

if ( 'core/image' !== name ) {
    return settings;
}

const newTransforms = [];
settings.transforms.from.forEach(( transformObj ) => {
    if ( 'raw' === transformObj.type ) {
        const origTransform = transformObj.transform;
        const transformWrap = ( node ) => {

            // Parse the Pinterest attributes and add to our central store.
            const img = node.querySelector( 'img' );
            const className = node.className + ' ' + img.className;
            const idMatches = /(?:^|\s)wp-image-(\d+)(?:$|\s)/.exec( className );
            const id = idMatches ? Number( idMatches[ 1 ] ) : undefined;
            if ( id ) {
                const valueKey = 'image_' + id;
                // img.dataset['pin-description'] is undefined because it's already been sanitized
                setStoreValue( 'descriptions', valueKey, img.dataset['pin-description'] );
            }

            return origTransform( node );
        };
        transformObj = Object.assign( transformObj, {
            transform: transformWrap,
        });
    } else if ( 'shortcode' === transformObj.type ) {
        // todo handle this too
    }
    newTransforms.push( transformObj );
});
settings.transforms.from = newTransforms;

However, the node HTML is unexpectedly sanitized before the transformation is called, which means my data is already missing.

mcsf commented 6 years ago

HTML is unexpectedly sanitized before the transformation is called

@danielbachhuber, is this helpful?

https://github.com/WordPress/gutenberg/issues/8648#issuecomment-434781445

danielbachhuber commented 6 years ago

@danielbachhuber, is this helpful?

#8648 (comment)

Yes, I think this would solve the problem I ran into with https://github.com/WordPress/gutenberg/issues/8473#issuecomment-433135779. However, I want to see how #11440 transforms into actionable tasks before I spend too much time on this.

danielbachhuber commented 6 years ago

Along with https://github.com/WordPress/gutenberg/issues/10204#issuecomment-439254269, I've gone with this:

const { merge } = lodash;

const { hooks } = wp;

const pinAttributes = {
    pinterestText: {
        type: 'string',
        source: 'attribute',
        selector: 'img',
        attribute: 'data-pin-description',
        default: '',
    }
};

const modifyRegistration = ( settings, name ) => {

    if ( 'core/image' !== name ) {
        return settings;
    }

    // Register our attributes to the Image Block.
    settings.attributes = Object.assign( settings.attributes, pinAttributes );

    const imageAttributes = [
        'src',
        'alt',
        'data-pin-description',
    ];

    // Extend raw <img> HTML transformation.
    settings.transforms.from[0] = merge( settings.transforms.from[0], {
        schema: {
            figure: {
                children: {
                    a: {
                        children: {
                            img: {
                                attributes: imageAttributes
                            }
                        }
                    },
                    img: {
                        attributes: imageAttributes
                    }
                }
            }
        }
    } );

    // Extend [caption] shortcode transformation
    settings.transforms.from[2].attributes = Object.assign( settings.transforms.from[2].attributes, pinAttributes );

    return settings;
};

hooks.addFilter( 'blocks.registerBlockType', 'wp-tasty/tasty-pins', modifyRegistration );