bobbingwide / oik-clone

Clone content between sites
https://www.oik-plugins.com/oik-plugins/oik-clone-clone-your-wordpress-content/
GNU General Public License v2.0
5 stars 0 forks source link

Map post IDs in block attributes #38

Open bobbingwide opened 5 years ago

bobbingwide commented 5 years ago

From #24

The cloning logic will also need to be improved to detect post IDs stored in block attributes that will need to be mapped to different ID when cloned.

This problem has now occurred. When cloning the block definitions for the two advanced-gutenberg-blocks blocks that are dependent upon WooCommerce the productID attributes are not mapped.

Failure to map the IDs led to fatal errors in the blocks. The problem is compounded by the fact the WooCommerce may not be activated on either end of the clone... so the post IDs may not be recognised as mappable; get_post() might fail if the post type is not registered? Perhaps get_postmeta() can be used to find the mapping.

New logic will be needed to perform mapping of attributes which contain IDs.

bobbingwide commented 5 years ago

Proposed solution

Extend the logic in informal relationship mapping to:

  1. Support attributes in HTML comments
  2. Handle other keywords referencing post IDs. E.g. ProductID. Consider using the commented out filter to allow the set to be extended.
bobbingwide commented 5 years ago

To satisfy the requirement to cater for get_post() not always working we would need to change admin/class-oik-clone-mapping.php

bobbingwide commented 4 years ago

I made a change to support productID in shortcodes but this doesn't work for the block's attributes in the HTML comment. e.g.

<!-- wp:advanced-gutenberg-blocks/product {
"productID":2184,
"priceColor":"#dd1e35",
"buttonBackgroundColor":"#ff6900"
} /-->\n\n

The parsing logic in class-oik-clone-informal-relationships.php need to handle blocks. Assume we can use block parsing logic to do this?

bobbingwide commented 4 years ago

It may also be necessary to parse the block comments to discover URLs which need changing Here's an example of a cover block which stores both the post ID of the selected attachment and its URL.

Note the nested block that makes it a bit more of a challenge!

<!-- wp:cover {"url":"https://core.wp-a2z.org/wp-content/uploads/sites/2/2016/07/wordpress-core-icon-256x256.jpg",
"id":15086,
"className":"aligncenter"} -->
<div class="wp-block-cover has-background-dim aligncenter" style="background-image:url(https://core.wp-a2z.org/wp-content/uploads/sites/2/2016/07/wordpress-core-icon-256x256.jpg)">
<div class="wp-block-cover__inner-container">
<!-- wp:paragraph {"align":"center","placeholder":"Write title…","fontSize":"large"} -->
<p class="has-text-align-center has-large-font-size">WordPress core</p>
<!-- /wp:paragraph -->
</div>
</div>
<!-- /wp:cover -->
bobbingwide commented 4 years ago

The output produced by the parse_blocks core function is an array like this. Each innerBlocks can be a nested array. So logic has to be recursive.

/**
 Array
(
[blockName] => core/cover
[attrs] => Array
    (
    [url] => https://core.wp-a2z.org/wp-content/uploads/sites/2/2016/07/wordpress-core-icon-256x256.jpg
    [id] => 15086
    [className] => aligncenter
    )

[innerBlocks] => Array
    (
    [0] => Array
    (
    [blockName] => core/paragraph
    [attrs] => Array
    (
    [align] => center
    [placeholder] => Write title…
    [fontSize] => large
    )

    [innerBlocks] => Array
    (
    )

    [innerHTML] =>
    <p class="has-text-align-center has-large-font-size">WordPress core</p>

    [innerContent] => Array
    (
    [0] =>
    <p class="has-text-align-center has-large-font-size">WordPress core</p>

    )

    )

    )

[innerHTML] =>
<div class="wp-block-cover has-background-dim aligncenter" style="background-image:url(https://core.wp-a2z.org/wp-content/uploads/sites/2/2016/07/wordpress-core-icon-256x256.jpg)"><div class="wp-block-cover__inner-container"></div></div>

[innerContent] => Array
    (
    [0] =>
    <div class="wp-block-cover has-background-dim aligncenter" style="background-image:url(https://core.wp-a2z.org/wp-content/uploads/sites/2/2016/07/wordpress-core-icon-256x256.jpg)"><div class="wp-block-cover__inner-container">
    [1] =>
    [2] => </div></div>

    )

)

The content may consist of a mixture of WordPress blocks and other content. The other content is parsed into blocks that don't have a value for blockName. These are classic or freeform blocks. When the blockName is null then reforming the blocks should not produce the HTML comments that contains the block name and JSON string of attrs.

bobbingwide commented 4 years ago

I've committed the first stab at implementing this.

The PHP Unit tests need to be enhanced to include

  1. Nested blocks with IDs in attrs
  2. Mapping of multiple IDs
  3. Mapping of IDs which aren't the same length
  4. What appears to be cyclic mapping of IDs

After that, we need to implement the server logic with functions that should already have been tested.

bobbingwide commented 4 years ago

Here's an example which shows that it's important to know which attrs contain post IDs that we can fiddle with. It also contains post IDs in nested blocks.

<!-- wp:columns -->\n<div class="wp-block-columns">
<!-- wp:column {"width":81} -->\n<div class="wp-block-column" style="flex-basis:81%">
<!-- wp:image {"id":3263,"sizeSlug":"large"} -->\n
<figure class="wp-block-image size-large">
<img src="https://s.b/wordpress/wp-content/uploads/2019/05/Marmalade-bored-1.jpg" 
alt="Two cats - two stools" 
class="wp-image-3263"/>
<figcaption>Singing for your dinner?\r\nOr are you just bored?\r\n</figcaption></figure>\n
<!-- /wp:image -->
</div>\n<!-- /wp:column -->\n\n
<!-- wp:column {"width":19} -->\n<div class="wp-block-column" style="flex-basis:19%">
<!-- wp:paragraph -->\n<p>This is the right hand column.</p>\n
<!-- /wp:paragraph --></div>\n
<!-- /wp:column --></div>\n
<!-- /wp:columns -->
bobbingwide commented 4 years ago

In the example for the Image block (wp:image = core/image) we also need to change the HTML generated by the block during the clone in order to set the correct value for the class= parameter on the <img> tag.
If we don’t, the block editor will throw a wobbler.

Workaround

Choose the Recover option, not the ones it first offers.

Solution

When we map an attr value from source to target is it also fair game to perform a str_replace() on the innerHTML and/or innerContent? It looks like we can do this in the map_attrs_ids method.

Answer: Yes. That appeared to work, so long as we handle each entry in innerContent correctly.

Note: There is a danger of cyclic mapping, but I think it’s an extreme edge case. And we still need to cater for multiple IDs.

bobbingwide commented 4 years ago

Well it nearly worked. The Media and Text block uses an attribute name of mediaId. I'll have to update the list of valid tokens.

bobbingwide commented 4 years ago

And now I have to consider providing mappings for taxonomy term IDs. In the woocommerce/featured-category block there is an attribute called categoryId. The value is the ID of product_cat category.

bobbingwide commented 4 years ago

Still finding more attributes to add. WooCommerce uses productId - with a lower case d.

bobbingwide commented 4 years ago

So far I've found two instances where there's more than one ID in an attribute. Examples:

<!-- wp:woocommerce/handpicked-products {"columns":2,"editMode":false,"products":[2184,2183]} /-->
<!-- wp:kadence/advancedgallery {"uniqueID":"_becf9d-fc","ids":[1816,2084,2185]} -->

They cause two problems

  1. They're not being mapped correctly.
  2. The [clone] shortcode doesn't work... see #43

We also have to cater for the first example which doesn't have any innerContent and is therefore a self closing HTML comment.

bobbingwide commented 4 years ago

The reusable block uses the ref attr to identify the wp_block post

<!-- wp:block {"ref":356} /-->
bobbingwide commented 3 years ago

I've released oik-clone v2.0.0 so going to close this issue. If we come across another block which has problems I can re-open it, or raise another.

bobbingwide commented 3 years ago

From https://github.com/bobbingwide/oik-clone/issues/38#issuecomment-541344430

And now I have to consider providing mappings for taxonomy term IDs. In the woocommerce/featured-category block there is an attribute called categoryId. The value is the ID of product_cat category.

Caught out by this issue at a live demo at WordCamp Europe earlier today. It's a problem that I know about, but had not fixed. I'd manually corrected the query but then I'd cloned the post again and broken it since the categoryIds values are different between Dev ( s.b/wp55/thisis ) and Live ( sneak-peek.me ).

Category categoryIds dev categoryIds live
FSE is... 4 2
FAQs 8 4

The code should also take into account tagIds. Note: categoryIds is the Gutenberg core attr name for Post Categories, taxonomy=category and categoryId is WooCommerce's attr name for the taxonomy=product_cat

It would appear that the mapping logic would need to know the taxonomy being mapped and this may depend on the attr & block name combination.

Note: It's not the same logic as mapping taxonomy terms since the taxonomies being mapped may not be associated with the post being cloned. In these examples the content being cloned is a page, but the query is referring to posts.

It would appear that in order to deal with this problem we first have to implement #32