woocommerce / woocommerce-blocks

(Deprecated) This plugin has been merged into woocommerce/woocommerce
https://wordpress.org/plugins/woo-gutenberg-products-block/
GNU General Public License v3.0
404 stars 218 forks source link

Cart block - Third party plugin compatibility - cart item formatting removed #10435

Closed marcusig closed 1 year ago

marcusig commented 1 year ago

Describe the bug

Custom cart item meta looses its formatting.

To reproduce

Steps to reproduce the behavior:

  1. Go to https://demos.mklacroix.com/custom-laptop/
  2. Select some options
  3. Add to cart
  4. Visit the cart comparison page (https://demos.mklacroix.com/cart-block-vs-cart-shortcode/)

Expected behavior

If you compare the item in the cart shortcode and the cart block, you'll see that in the first one, the options are formatted properly, while in the block, all html is stripped.

Please advise whether there is a way to prevent a specific entry from being stripped, or if an other approach is advised.

Screenshots

Cart Shortcode:

image

Cart Block:

image

WordPress

Additional context

As the developper of the plugins mentioned above, this issue is part of a series of issues brought forward in an effort to make them compatible with the cart and checkout blocks.

nielslange commented 1 year ago

Thanks for reaching out, @marcusig. Can you share a code snippet to reproduce this issue or can you share the Extra Price add-on via email with us?

marcusig commented 1 year ago

Hi @nielslange, thanks for looking into this.

I've updated the issue description, as the formatting goes no matter the add-ons used. You can test this with the Configurator plugin itself.

Here's a demo content which can be used: custom-chair.json.zip

The data is set using the filter woocommerce_get_item_data.

This is the method: https://github.com/marcusig/woocommerce-product-customizer/blob/master/src/inc/frontend/cart.php#L129

I did look at the WooCommerce code, and I saw that the cart item data is stripped of all html when requested via the Store API, and I didn't find a way to override that.

senadir commented 1 year ago

HTML is stripped as a measure of security and to preserve the layout/style of the cart. As meta data is only supposed to be a key: value of strings.

What kind of HTML are you trying to do?

marcusig commented 1 year ago

@senadir It's a kind of nested meta data. E.g.

<dl class="variation">
    <dt class="variation-Configuration">Configuration:</dt>
    <dd class="variation-Configuration">
        <div class="simple"><strong>Processor</strong><span class="semicol">:</span>
            <span class="mkl_pc-choice-value">
                <span class="choice-thumb">
                    <img
                        src="http://localhost:10036/wp-content/uploads/2021/06/Function_PWR_Icon-140x140-1.png"
                        alt>
                </span>
                <span class="mkl_pc-choice-name">M1</span>
                <span class="mkl_pc-extra-price">(<span class="mkl_pc-extra-price--plus">+</span><span class="woocommerce-Price-amount amount">20,00&nbsp;<span class="woocommerce-Price-currencySymbol">€</span></span>)</span>
            </span>
        </div>
        [... more items like above]
        <div class="mkl-pc-edit-link--container"><a
                href="http://localhost:10036/product/my-laptop/?load_config_from_cart=1136c39dc02775596e221c45f85703d7&amp;open_configurator=1&amp;edit_config_from_cart=1"
                class="mkl-pc--edit-configuration">Edit configuration</a></div>
    </dd>
</dl>

Which outputs something like seen in the screenshot above.

marcusig commented 1 year ago

If there is no possibility to change this, then I would like a suggestion on how I can handle this.

senadir commented 1 year ago

Understandable, configuration and interacting UI at Cart Item level is something we're planning to add soon. The only thing I can suggest now is to loop over your data and add it pair by pair

image
marcusig commented 1 year ago

Thanks for your feedback.

The only thing I can suggest now is to loop over your data and add it pair by pair

This has a few drawbacks:

senadir commented 1 year ago

I appreciate the feedback, those are things we will try to figure out.

I'm not sure if we're going to tackle adding images thou, the separation make sense, and adding links make sense. The pairing does provide custom classes for each key for example wc-block-components-product-details__memory or via a className prop you pass along in woocommerce_get_item_data

marcusig commented 1 year ago

Thanks, I'll look into this.

What is the recommended way for me to check that the data is being fetched from the Store API, so that I can send the appropriate data only for the blocks and not for the shortcode?

And is there currently no other generic way to interact with the CartLineItemRow (apart from the existing filters) at render time?

mikejolley commented 1 year ago

wc()->is_rest_api_request() would return true for any API request, or you can look at $_SERVER['REQUEST_URI'].

And is there currently no other generic way to interact with the CartLineItemRow (apart from the existing filters) at render time?

At the moment there is not, but its one of the things being looked at in the coming months, potentially including a new slot/fill for including custom data at render time. That could then be used in tandem with data from the Store API.

I will mark this issue closed for now because of the security impact of allowing HTML in these fields, but rest assured this is one thing on our roadmap to tackle.

marcusig commented 1 year ago

@mikejolley Thank you for the feedback.

its one of the things being looked at in the coming months, potentially including a new slot/fill for including custom data at render time. That could then be used in tandem with data from the Store API.

I'm hoping this (as well as the other issues I opened) will be looked at before the November release where the blocks will be the default experience :)

marcusig commented 1 year ago

For anyone needing a different behaviour in the cart block and the cart shortcode, wc()->is_rest_api_request() isn't enough, because at the initial load of the page, the block data is hydrated without using the rest API. My solution (probably not the best) is to use debug_backtrace to find out if CartItemSchema is in the call stack:

class MyCartData {
    public function __construct() {
        add_filter( 'woocommerce_get_item_data', array( $this, 'wc_cart_get_item_data' ), 10, 2 ); 
    }

    public function wc_cart_get_item_data( $data, $cart_item ) {
        if ( 'block' == $this->_get_cart_item_context() ) {
            $data['block-specific-data'] = 'foo';
        } else {
            $data['shortcode-specific-data'] = 'bar';
        }
        return $data;
    }

    private function _get_cart_item_context() {
        $trace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 5 );
        foreach( $trace as $call ) {
            // [class] => Automattic\WooCommerce\StoreApi\Schemas\V1\CartItemSchema
            if ( isset( $call['class'] ) && false !== strpos( $call['class'], 'CartItemSchema' ) ) {
                return 'block';
            }
        }
        return 'default';
    }
}