Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add/navigation blocks post processing after migration from menu items #36950

Conversation

adamziel
Copy link
Contributor

@adamziel adamziel commented Nov 29, 2021

Description

Related to #36616

Problem:

  1. Polylang (and many other plugins) has a special menu item called "Language switcher". Whenever a menu is rendered or returned by an API endpoint, that menu item is turned into many items like "English, French, Spanish" by a filter..
  2. Gutenberg migrates menu items to blocks one by one based on the REST API output. Language switcher is represented as many links, so instead of ending up with a single "language switcher" block, it ends up with one navigation link block per language.

Proposed solution:
Add a set od filters to postprocess the menu items -> blocks conversion results. A usage example for the Polylang plugin would look like this:

Sample usage in JS (runs after selecting a classic menu in the block):

wp.hooks.addFilter(
	'navigation.menuItemsToBlocks',
	'polylang/include-language-picker',
	( navBlock, menuItems ) => ({
		...navBlock,
		innerBlocks: mapBlockTree(
			navBlock.innerBlocks,
			block => {
				if(block.name === "core/navigation-link" && block.attributes?.url === "#pll_switcher") {
					return createBlock( 'polylang/navigation-language-switcher' );
				}
				return block;
			}
		)
	})
);

function mapBlockTree(blocks, mapper) {
    return blocks.map(block => ({
        ...mapper(block),
        innerBlocks: mapBlockTree(block.innerBlocks, mapper)
    }));
}

A corresponding filter would have to be implemented in PHP to catch the conversion that runs when switching to a block theme.

As a side note, the migration could potentially be handled entirely on the PHP side instead of being implemented in both PHP and JS – but that's for another day. cc @noisysocks @talldan @getdave @manooweb

How has this been tested?

Confirm that all the checks are green.

@github-actions
Copy link

github-actions bot commented Nov 29, 2021

Size Change: +1.91 kB (0%)

Total Size: 1.11 MB

Filename Size Change
build/block-editor/index.min.js 139 kB +51 B (0%)
build/block-editor/style-rtl.css 14.4 kB +2 B (0%)
build/block-editor/style.css 14.4 kB +2 B (0%)
build/block-library/blocks/image/editor-rtl.css 810 B +79 B (+11%) ⚠️
build/block-library/blocks/image/editor.css 809 B +79 B (+11%) ⚠️
build/block-library/blocks/post-comments-form/style-rtl.css 446 B +2 B (0%)
build/block-library/blocks/post-comments-form/style.css 446 B +2 B (0%)
build/block-library/blocks/post-comments/style-rtl.css 507 B +15 B (+3%)
build/block-library/blocks/post-comments/style.css 507 B +14 B (+3%)
build/block-library/blocks/pullquote/style-rtl.css 389 B +11 B (+3%)
build/block-library/blocks/pullquote/style.css 388 B +10 B (+3%)
build/block-library/editor-rtl.css 10 kB +75 B (+1%)
build/block-library/editor.css 10 kB +75 B (+1%)
build/block-library/index.min.js 163 kB +259 B (0%)
build/block-library/style-rtl.css 10.7 kB +15 B (0%)
build/block-library/style.css 10.8 kB +14 B (0%)
build/components/index.min.js 215 kB +12 B (0%)
build/edit-post/index.min.js 29.7 kB +59 B (0%)
build/edit-site/index.min.js 35.4 kB +1.15 kB (+3%)
build/edit-site/style-rtl.css 6.57 kB -6 B (0%)
build/edit-site/style.css 6.57 kB -7 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 960 B
build/admin-manifest/index.min.js 1.1 kB
build/annotations/index.min.js 2.75 kB
build/api-fetch/index.min.js 2.21 kB
build/autop/index.min.js 2.12 kB
build/blob/index.min.js 459 B
build/block-directory/index.min.js 6.28 kB
build/block-directory/style-rtl.css 1.01 kB
build/block-directory/style.css 1.01 kB
build/block-editor/default-editor-styles-rtl.css 378 B
build/block-editor/default-editor-styles.css 378 B
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 65 B
build/block-library/blocks/archives/style.css 65 B
build/block-library/blocks/audio/editor-rtl.css 58 B
build/block-library/blocks/audio/editor.css 58 B
build/block-library/blocks/audio/style-rtl.css 111 B
build/block-library/blocks/audio/style.css 111 B
build/block-library/blocks/audio/theme-rtl.css 125 B
build/block-library/blocks/audio/theme.css 125 B
build/block-library/blocks/block/editor-rtl.css 161 B
build/block-library/blocks/block/editor.css 161 B
build/block-library/blocks/button/editor-rtl.css 470 B
build/block-library/blocks/button/editor.css 470 B
build/block-library/blocks/button/style-rtl.css 560 B
build/block-library/blocks/button/style.css 560 B
build/block-library/blocks/buttons/editor-rtl.css 291 B
build/block-library/blocks/buttons/editor.css 291 B
build/block-library/blocks/buttons/style-rtl.css 275 B
build/block-library/blocks/buttons/style.css 275 B
build/block-library/blocks/calendar/style-rtl.css 207 B
build/block-library/blocks/calendar/style.css 207 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 79 B
build/block-library/blocks/categories/style.css 79 B
build/block-library/blocks/code/style-rtl.css 90 B
build/block-library/blocks/code/style.css 90 B
build/block-library/blocks/code/theme-rtl.css 134 B
build/block-library/blocks/code/theme.css 134 B
build/block-library/blocks/columns/editor-rtl.css 210 B
build/block-library/blocks/columns/editor.css 208 B
build/block-library/blocks/columns/style-rtl.css 503 B
build/block-library/blocks/columns/style.css 502 B
build/block-library/blocks/comment-template/style-rtl.css 127 B
build/block-library/blocks/comment-template/style.css 127 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/cover/editor-rtl.css 546 B
build/block-library/blocks/cover/editor.css 547 B
build/block-library/blocks/cover/style-rtl.css 1.22 kB
build/block-library/blocks/cover/style.css 1.22 kB
build/block-library/blocks/embed/editor-rtl.css 488 B
build/block-library/blocks/embed/editor.css 488 B
build/block-library/blocks/embed/style-rtl.css 417 B
build/block-library/blocks/embed/style.css 417 B
build/block-library/blocks/embed/theme-rtl.css 124 B
build/block-library/blocks/embed/theme.css 124 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 255 B
build/block-library/blocks/file/style.css 255 B
build/block-library/blocks/file/view.min.js 322 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 966 B
build/block-library/blocks/gallery/editor.css 970 B
build/block-library/blocks/gallery/style-rtl.css 1.63 kB
build/block-library/blocks/gallery/style.css 1.62 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 159 B
build/block-library/blocks/group/editor.css 159 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 114 B
build/block-library/blocks/heading/style.css 114 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/style-rtl.css 507 B
build/block-library/blocks/image/style.css 511 B
build/block-library/blocks/image/theme-rtl.css 124 B
build/block-library/blocks/image/theme.css 124 B
build/block-library/blocks/latest-comments/style-rtl.css 284 B
build/block-library/blocks/latest-comments/style.css 284 B
build/block-library/blocks/latest-posts/editor-rtl.css 137 B
build/block-library/blocks/latest-posts/editor.css 137 B
build/block-library/blocks/latest-posts/style-rtl.css 528 B
build/block-library/blocks/latest-posts/style.css 527 B
build/block-library/blocks/list/style-rtl.css 94 B
build/block-library/blocks/list/style.css 94 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 493 B
build/block-library/blocks/media-text/style.css 490 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 649 B
build/block-library/blocks/navigation-link/editor.css 650 B
build/block-library/blocks/navigation-link/style-rtl.css 94 B
build/block-library/blocks/navigation-link/style.css 94 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation-submenu/view.min.js 343 B
build/block-library/blocks/navigation/editor-rtl.css 1.91 kB
build/block-library/blocks/navigation/editor.css 1.91 kB
build/block-library/blocks/navigation/style-rtl.css 1.68 kB
build/block-library/blocks/navigation/style.css 1.67 kB
build/block-library/blocks/navigation/view.min.js 2.79 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 377 B
build/block-library/blocks/page-list/editor.css 377 B
build/block-library/blocks/page-list/style-rtl.css 172 B
build/block-library/blocks/page-list/style.css 172 B
build/block-library/blocks/paragraph/editor-rtl.css 157 B
build/block-library/blocks/paragraph/editor.css 157 B
build/block-library/blocks/paragraph/style-rtl.css 273 B
build/block-library/blocks/paragraph/style.css 273 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 721 B
build/block-library/blocks/post-featured-image/editor.css 721 B
build/block-library/blocks/post-featured-image/style-rtl.css 153 B
build/block-library/blocks/post-featured-image/style.css 153 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 391 B
build/block-library/blocks/post-template/style.css 392 B
build/block-library/blocks/post-terms/style-rtl.css 73 B
build/block-library/blocks/post-terms/style.css 73 B
build/block-library/blocks/post-title/style-rtl.css 80 B
build/block-library/blocks/post-title/style.css 80 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 198 B
build/block-library/blocks/pullquote/editor.css 198 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 273 B
build/block-library/blocks/query-pagination/style.css 269 B
build/block-library/blocks/query/editor-rtl.css 131 B
build/block-library/blocks/query/editor.css 132 B
build/block-library/blocks/quote/style-rtl.css 187 B
build/block-library/blocks/quote/style.css 187 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/rss/editor-rtl.css 202 B
build/block-library/blocks/rss/editor.css 204 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 397 B
build/block-library/blocks/search/style.css 398 B
build/block-library/blocks/search/theme-rtl.css 64 B
build/block-library/blocks/search/theme.css 64 B
build/block-library/blocks/separator/editor-rtl.css 99 B
build/block-library/blocks/separator/editor.css 99 B
build/block-library/blocks/separator/style-rtl.css 245 B
build/block-library/blocks/separator/style.css 245 B
build/block-library/blocks/separator/theme-rtl.css 172 B
build/block-library/blocks/separator/theme.css 172 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 772 B
build/block-library/blocks/site-logo/editor.css 772 B
build/block-library/blocks/site-logo/style-rtl.css 165 B
build/block-library/blocks/site-logo/style.css 165 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 84 B
build/block-library/blocks/site-title/editor.css 84 B
build/block-library/blocks/social-link/editor-rtl.css 177 B
build/block-library/blocks/social-link/editor.css 177 B
build/block-library/blocks/social-links/editor-rtl.css 670 B
build/block-library/blocks/social-links/editor.css 669 B
build/block-library/blocks/social-links/style-rtl.css 1.32 kB
build/block-library/blocks/social-links/style.css 1.32 kB
build/block-library/blocks/spacer/editor-rtl.css 307 B
build/block-library/blocks/spacer/editor.css 307 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 471 B
build/block-library/blocks/table/editor.css 472 B
build/block-library/blocks/table/style-rtl.css 481 B
build/block-library/blocks/table/style.css 481 B
build/block-library/blocks/table/theme-rtl.css 188 B
build/block-library/blocks/table/theme.css 188 B
build/block-library/blocks/tag-cloud/style-rtl.css 146 B
build/block-library/blocks/tag-cloud/style.css 146 B
build/block-library/blocks/template-part/editor-rtl.css 560 B
build/block-library/blocks/template-part/editor.css 559 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 87 B
build/block-library/blocks/verse/style.css 87 B
build/block-library/blocks/video/editor-rtl.css 569 B
build/block-library/blocks/video/editor.css 570 B
build/block-library/blocks/video/style-rtl.css 173 B
build/block-library/blocks/video/style.css 173 B
build/block-library/blocks/video/theme-rtl.css 124 B
build/block-library/blocks/video/theme.css 124 B
build/block-library/common-rtl.css 857 B
build/block-library/common.css 856 B
build/block-library/reset-rtl.css 474 B
build/block-library/reset.css 474 B
build/block-library/theme-rtl.css 672 B
build/block-library/theme.css 677 B
build/block-serialization-default-parser/index.min.js 1.09 kB
build/block-serialization-spec-parser/index.min.js 2.79 kB
build/blocks/index.min.js 46.3 kB
build/components/style-rtl.css 15.5 kB
build/components/style.css 15.5 kB
build/compose/index.min.js 10.9 kB
build/core-data/index.min.js 13.2 kB
build/customize-widgets/index.min.js 11.4 kB
build/customize-widgets/style-rtl.css 1.5 kB
build/customize-widgets/style.css 1.49 kB
build/data-controls/index.min.js 631 B
build/data/index.min.js 7.49 kB
build/date/index.min.js 31.5 kB
build/deprecated/index.min.js 485 B
build/dom-ready/index.min.js 304 B
build/dom/index.min.js 4.5 kB
build/edit-navigation/index.min.js 16 kB
build/edit-navigation/style-rtl.css 3.76 kB
build/edit-navigation/style.css 3.76 kB
build/edit-post/classic-rtl.css 492 B
build/edit-post/classic.css 494 B
build/edit-post/style-rtl.css 7.1 kB
build/edit-post/style.css 7.09 kB
build/edit-widgets/index.min.js 16.5 kB
build/edit-widgets/style-rtl.css 4.18 kB
build/edit-widgets/style.css 4.18 kB
build/editor/index.min.js 37.8 kB
build/editor/style-rtl.css 3.78 kB
build/editor/style.css 3.77 kB
build/element/index.min.js 3.29 kB
build/escape-html/index.min.js 517 B
build/format-library/index.min.js 6.58 kB
build/format-library/style-rtl.css 571 B
build/format-library/style.css 571 B
build/hooks/index.min.js 1.63 kB
build/html-entities/index.min.js 424 B
build/i18n/index.min.js 3.71 kB
build/is-shallow-equal/index.min.js 501 B
build/keyboard-shortcuts/index.min.js 1.8 kB
build/keycodes/index.min.js 1.39 kB
build/list-reusable-blocks/index.min.js 1.72 kB
build/list-reusable-blocks/style-rtl.css 838 B
build/list-reusable-blocks/style.css 838 B
build/media-utils/index.min.js 2.92 kB
build/notices/index.min.js 925 B
build/nux/index.min.js 2.08 kB
build/nux/style-rtl.css 747 B
build/nux/style.css 743 B
build/plugins/index.min.js 1.84 kB
build/primitives/index.min.js 924 B
build/priority-queue/index.min.js 582 B
build/react-i18n/index.min.js 671 B
build/redux-routine/index.min.js 2.65 kB
build/reusable-blocks/index.min.js 2.22 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 11 kB
build/server-side-render/index.min.js 1.57 kB
build/shortcode/index.min.js 1.49 kB
build/token-list/index.min.js 639 B
build/url/index.min.js 1.9 kB
build/viewport/index.min.js 1.05 kB
build/warning/index.min.js 248 B
build/widgets/index.min.js 7.15 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.04 kB

compressed-size-action

@manooweb
Copy link
Contributor

manooweb commented Dec 1, 2021

Thanks a lot @adamziel to have been work on it 👍 and the lead.
I tested on my side and it seems the filter added in js code is sufficient at this step of my test.

However, I already found a mistake in the sample code provided

	return {
		name: "navigation-language-switcher",
		clientId: block.clientId,
	};

We don't return a block here and it triggers an error later in the execution

serializer.js:216 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'attributes')
    at getCommentAttributes (serializer.js:216)
    at serializeBlock (serializer.js:357)
    at serializer.js:393
    at Array.map (<anonymous>)
    at serialize (serializer.js:393)
    at use-create-navigation-menu.js:27
    at onFinishMenuCreation (index.js:105)
    at index.js:127
    at index.js:158
    at invokePassiveEffectCreate (react-dom.development.js:23517)

I simply replaced it by a call to @wordpress/blocks createBlock() method like

	if( block.name === "core/navigation-link" && block.attributes?.url === "#pll_switcher" ) {
		return createBlock( 'polylang/navigation-language-switcher' );
	}

for example

It works fine like that and now it's the language switcher block which appears as a child of the navigation block
image

However the language switcher block attributes isn't correctly set because the language switcher menu item options are a custom post meta which isn't returned by default.

I will take a look at the navigation_after_parse_blocks_from_menu_items added to lib/navigation.php if it allow us to solve this problem.

I will let you know

@adamziel
Copy link
Contributor Author

adamziel commented Dec 1, 2021

@manooweb you're bringing up a good point here about the custom attributes. I think the menu migration would have to PHP-only to support that. I imagine that would involve a new endpoint that would run the migration code that's already there. By the way, @talldan mentioned removing the PHP filter that runs on theme switch in #36604 (comment), which adds urgency to this issue.

However, I already found a mistake in the sample code provided

Good catch, thanks! I just updated that snippet

@manooweb
Copy link
Contributor

manooweb commented Dec 1, 2021

@manooweb you're bringing up a good point here about the custom attributes. I think the menu migration would have to PHP-only to support that. I imagine that would involve a new endpoint that would run the migration code that's already there. By the way, @talldan mentioned removing the PHP filter that runs on theme switch in #36604 (comment), which adds urgency to this issue.

@adamziel it seems to me that it isn't the context we're using.
Indeed menu items are retrieved by a REST request with the new menu-items endpoint https://github.com/WordPress/gutenberg/blob/v12.1.0-rc.1/lib/class-wp-rest-menu-items-controller.php#L34-L39

I'm going to search in that direction instead. We're used to use the REST API for the language for example and it seems not to be difficult to push our custom post meta here.

lib/navigation.php Outdated Show resolved Hide resolved
@manooweb
Copy link
Contributor

manooweb commented Dec 6, 2021

On my side, I studied a little bit further espacially because we didn't get the Polylang language switcher menu item meta (_pll_menu_item).

So I register the meta to be able to get it when the menu-items endpoint is called during the conversion process.

<?php
add_action( 'rest_api_init', 'pll_register_switcher_menu_item_options_meta_rest_field' );

function pll_register_switcher_menu_item_options_meta_rest_field() {
    $return = register_meta(
        'post',
        '_pll_menu_item',
        array(
            'object_subtype' => 'nav_menu_item',
            'description' => __( 'Language switcher menu item options.', 'polylang-pro' ),
            'single' => true,
            'show_in_rest' => array(
                'schema' => array(
                    'type' => 'object',
                    'additionalProperties' => array(
                        'type' => 'integer',
                    ),
                )
            ),
        )
    );
}

and I got correctly the meta with the menu-items REST API call.

{
    "id": 585,
    "title": {
        "raw": "Langues",
        "rendered": "Langues"
    },
    "status": "publish",
    "url": "#pll_switcher",
    "attr_title": "",
    "description": "",
    "type": "custom",
    "type_label": "Liste des langues",
    "object": "custom",
    "object_id": 585,
    "content": {
        "raw": "",
        "rendered": "",
        "block_version": 0
    },
    "parent": 0,
    "menu_order": 1,
    "target": "",
    "classes": [
        ""
    ],
    "xfn": [
        ""
    ],
    "invalid": false,
    "meta": {
        "_pll_menu_item": {
            "hide_if_no_translation": 0,
            "hide_current": 0,
            "force_home": 0,
            "show_flags": 1,
            "show_names": 1,
            "dropdown": 0
        }
    },
    "menus": 66,
    "_links": {
        "self": [
            {
                "href": "http://support.local/wp-json/wp/v2/menu-items/585"
            }
        ],
        "collection": [
            {
                "href": "http://support.local/wp-json/wp/v2/menu-items"
            }
        ],
        "about": [
            {
                "href": "http://support.local/wp-json/wp/v2/types/nav_menu_item"
            }
        ],
        "wp:term": [
            {
                "taxonomy": "nav_menu",
                "embeddable": true,
                "href": "http://support.local/wp-json/wp/v2/menus?post=585"
            }
        ],
        "wp:action-publish": [
            {
                "href": "http://support.local/wp-json/wp/v2/menu-items/585"
            }
        ],
        "wp:action-unfiltered-html": [
            {
                "href": "http://support.local/wp-json/wp/v2/menu-items/585"
            }
        ],
        "wp:action-create-menus": [
            {
                "href": "http://support.local/wp-json/wp/v2/menu-items/585"
            }
        ],
        "wp:action-assign-menus": [
            {
                "href": "http://support.local/wp-json/wp/v2/menu-items/585"
            }
        ],
        "curies": [
            {
                "name": "wp",
                "href": "https://api.w.org/{rel}",
                "templated": true
            }
        ]
    }
}

Then I adapted the code provided, to pass the meta to the block creation
NB: find is the lodash function of course.

function mapBlockTree( blocks, menuItems, mapper ) {
	return blocks.map(
		block => (
			{
				...mapper( block, menuItems ),
				innerBlocks: mapBlockTree( block.innerBlocks, menuItems, mapper )
			}
		)
	 );;
}

addFilter(
	'navigation.menuItemsToBlocks',
	'polylang/include-language-switcher',
	( blocks, menuItems ) => (
		{
			...blocks,
			innerBlocks: mapBlockTree(
				blocks.innerBlocks,
				menuItems,
				( block, menuItems ) => {
					if( block.name === "core/navigation-link" && block.attributes?.url === "#pll_switcher" ) {
						const menuItem = find( menuItems, { url: '#pll_switcher' } ); // Get the corresponding menu item.
						const attributes = menuItem.meta._pll_menu_item; // Get its options.
						return createBlock( navigationLanguageSwitcherName, attributes );
					}
					return block;
				}
			)
	 	}
	)
);

However, even if the menu item got the correct meta during the process
image

The block attributes UI didn't display the right options. Display flags should be checked because the show_flags meta option is set to 1

image

I noticed that the wp_navigation post seems good to me in DB
image

image

So I thought because the navigation block mapping wasn't updated. Then I adapted the code again to take the blocks mapping with the new language switcher block client id.

function mapBlockTree( blocks, menuItems, blocksMapping, mapper ) {
	return blocks.map(
		block => (
			{
				...mapper( block, menuItems, blocksMapping ),
				innerBlocks: mapBlockTree( block.innerBlocks, menuItems, blocksMapping, mapper )
			}
		)
	 );;
}

addFilter(
	'navigation.menuItemsToBlocks',
	'polylang/include-language-switcher',
	( blocks, menuItems ) => (
		{
			...blocks,
			innerBlocks: mapBlockTree(
				blocks.innerBlocks,
				menuItems,
				blocks.mapping,
				( block, menuItems, blocksMapping ) => {
					if( block.name === "core/navigation-link" && block.attributes?.url === "#pll_switcher" ) {
						const menuItem = find( menuItems, { url: '#pll_switcher' } ); // Get the corresponding menu item.
						const attributes = menuItem.meta._pll_menu_item; // Get its options.
						const newBlock = createBlock( navigationLanguageSwitcherName, attributes );
						blocksMapping[ menuItem.id ] = newBlock.clientId;
						return newBlock;
					}
					return block;
				}
			)
	 	}
	)
);

Unfortunately it changed nothing to the result the language switcher block attributes aren't right in the UI.

I don't see how to figure out this issue and where to search.

If you have an idea I'm very interested in

Many thanks

@adamziel
Copy link
Contributor Author

adamziel commented Dec 6, 2021

@manooweb somehow I can't find the polylang/navigation-language-switcher anywhere which means I can't test. Was it released with the latest PolyLang version?

Anyway, let me take my best guess:
Block attributes tend to use camelCase, e.g. showFlags. However, the API returns snake-cased data, e.g. show_flags. Based on your debugger screenshot, I believe things are still snake-cased when we reach this fragment:

const attributes = menuItem.meta._pll_menu_item; // Get its options.
const newBlock = createBlock( navigationLanguageSwitcherName, attributes );

And so the call unwinds to

const newBlock = createBlock(
	"polylang/navigation-language-switcher",
	{
		dropdown: 0,
		force_home: 0,
		hide_current: 0,
		hide_if_no_translation: 0,
		show_flags: 1,
		show_names: 1,
	}
);

When I think you're after:

const newBlock = createBlock(
	"polylang/navigation-language-switcher",
	{
		dropdown: 0,
		forceHome: 0,
		hideCurrent: 0,
		hideIfNoTranslation: 0,
		showFlags: 1,
		showNames: 1,
	}
);

LMK if that helped.

@manooweb
Copy link
Contributor

manooweb commented Dec 6, 2021

@manooweb somehow I can't find the polylang/navigation-language-switcher anywhere which means I can't test. Was it released with the latest PolyLang version?

I'm aware it's difficult to understand without this part of the code but it is developed in the premium version only.
The language switcher block exists since several versions now.
I'm going to try to provide a piece of code to work with its free version.

Anyway, let me take my best guess: Block attributes tend to use camelCase, e.g. showFlags. However, the API returns snake-cased data, e.g. show_flags. Based on your debugger screenshot, I believe things are still snake-cased when we reach this fragment:

const attributes = menuItem.meta._pll_menu_item; // Get its options.
const newBlock = createBlock( navigationLanguageSwitcherName, attributes );

And so the call unwinds to

const newBlock = createBlock(
	"polylang/navigation-language-switcher",
	{
		dropdown: 0,
		force_home: 0,
		hide_current: 0,
		hide_if_no_translation: 0,
		show_flags: 1,
		show_names: 1,
	}
);

When I think you're after:

const newBlock = createBlock(
	"polylang/navigation-language-switcher",
	{
		dropdown: 0,
		forceHome: 0,
		hideCurrent: 0,
		hideIfNoTranslation: 0,
		showFlags: 1,
		showNames: 1,
	}
);

LMK if that helped.

I don't think it's a case problem with the attribute names. The block itself works correctly with these attributes in snake case.
image

image

We develop like that to be consistent with the language switcher in PHP code espacially to maintain these options only in one place.
https://github.com/polylang/polylang/blob/3.1.2/include/switcher.php#L45-L52

@manooweb
Copy link
Contributor

manooweb commented Dec 7, 2021

@adamziel I extracted in a GH repository the code necessary to have the language switcher block as a stand alone block or as a child of the core/navigation block.

You have a readme file to explain how to install it and how to use it with the second use case that we are interested in here.

https://github.com/manooweb/gutenberg-block-navigation-conversion-test

If you need any other explanation or have issues, LMK 😉

Thanks

@adamziel
Copy link
Contributor Author

adamziel commented Dec 7, 2021

@manooweb thanks! I identified the problem. The attributes are registered as boolean, but the data from the API is of type number. When I cast it to bool like attributes.dropdown = !! attributes.dropdown, it all works as expected. I suspect this is related to the block parser sanitizing the navigation post content it receives in response to the POST API request. Let me know if that helps.

@manooweb
Copy link
Contributor

manooweb commented Dec 7, 2021

@manooweb thanks! I identified the problem. The attributes are registered as boolean, but the data from the API is of type number. When I cast it to bool like attributes.dropdown = !! attributes.dropdown, it all works as expected. I suspect this is related to the block parser sanitizing the navigation post content it receives in response to the POST API request. Let me know if that helps.

@adamziel Yes!! That helps 👍 Thanks

@manooweb
Copy link
Contributor

manooweb commented Dec 8, 2021

@adamziel LGTM
I adapted our code like this https://github.com/manooweb/gutenberg-block-navigation-conversion-test/blob/ca02378870c8f69f49fd79a6d40645eb1e536e01/language-switcher-blocks.js#L157-L158

And now the result is as expected

From the classic menu, I checked Display flags option
image

and the conversion to the corresponding block in the core/navigation block works correctly
image

Edit: in fact it depends on how to declare REST API schema to retrieve the meta. I had declared integer because it's the real type saved in DB however we need to get a boolean value that corresponds really on its use.

So I reverted the JS code and I corrected the register_meta call with the correct type in the REST API schema.
https://github.com/manooweb/gutenberg-block-navigation-conversion-test/blob/acd27ef464f3bb1013dde0df67e3022e5c1ecdfd/block-editor-switcher-block.php#L327

Do you think this PR can be merged in the next release and available in the next WordPress release?

Many thanks for the work and time to help 👍😉

@adamziel
Copy link
Contributor Author

adamziel commented Dec 8, 2021

Do you think this PR can be merged in the next release and available in the next WordPress release?

@manooweb I believe so! I just need an approval here :-) @getdave @noisysocks @talldan @draganescu @Mamaduka anyone willing to approve ?

Copy link
Contributor

@getdave getdave left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and it's as I imagined this would be solved. Nice work!

I think we need to document this filter as I imagine folks would want to use it. Not sure where the best place for that is. Maybe @mkaz would know?

@draganescu
Copy link
Contributor

I think this is a smart solution to the problem but I have a few, potentially convoluted, concerns:

Will we ever want to transform classic menus into something else other than submenu and link? Adding this filter will be a contract that we always return submenu and link to extenders.

  • will a future deprecation of the edit component of the navigation block continue to work when plugins use this filter?

This is a 1st in the block library :)

  • is this all we need to do (add a filter) considering no other core block needs any filter other than the standard block filters?

Looking at the problem:

Language switcher is represented as many links, so instead of ending up with a single "language switcher" block, it ends up with one navigation link block per language.

  • Is is possible to make the filter be less low level, as what seems to be needed is a way to insert a special block (the langauge switcher block) in the navigation edit component made of some of the links already found in it (the language links)? A sort of auto-transform thing?

All my concerns are summed up by just being worried that by adding a filter on client side conversion we make a contract about the structure of the navigation block to the world.

@getdave
Copy link
Contributor

getdave commented Dec 9, 2021

I will defer to @adamziel who no doubt is better placed to answer than I. Nonetheless:

Will we ever want to transform classic menus into something else other than submenu and link? Adding this filter will be a contract that we always return submenu and link to extenders.

I don't think we are forcing users to return a particular structure. We're giving them the option to hook into each menn item and change how that is transformed into a block. If they want menu item X to turn into multiple blocks equivalents then that's their choice. They could just as easily do nothing or just subtly change the conversion. Indeed Adam's example shows you can just return block and do nothing.

All my concerns are summed up by just being worried that by adding a filter on client side conversion we make a contract about the structure of the navigation block to the world.

We are making a contract on that filter. I felt it was safe to assume that we will always want to provide a means of transforming menu items to blocks. If we're not 100% sure that's not going to be a feature then perhaps we need to make this filter experimental for a while and make a note to promote to first class only after a few Gutenberg Plugin versions have elapsed.

@manooweb
Copy link
Contributor

manooweb commented Dec 9, 2021

@draganescu, today we can extend "classic" menu with custom menu item and the current version of core/navigation block convert all the menu item from an existing "classic" menu only to a core/navigation-link which isn't the result expected.

We need to change the conversion behaviour to catch the custom menu item and change its conversion to another block than the default core/navigation-link block and then have the same render as we're using the initial "classic" menu.

@draganescu
Copy link
Contributor

@manooweb Yes, I understand the need and I do think this conversion should be changeable by extenders. My worry is about this solution because it is unique in the block library and because it may impact future updates to the block.

@adamziel
Copy link
Contributor Author

adamziel commented Dec 10, 2021

All my concerns are summed up by just being worried that by adding a filter on client side conversion we make a contract about the structure of the navigation block to the world.

Thanks for bringing it up @draganescu. I agree, I even wanted to explore backend-only conversion to blocks for 6.0. The usual way of navigating the uncertainty is to call things __unstable, so I renamed the filter to reflect that.

@adamziel adamziel added the Backport to WP Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Dec 10, 2021
Copy link
Contributor

@draganescu draganescu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With an unstable label this can move forward. I think we'll be better off in the future to remove client side filtering and instead allow extenders to append detail to the output of the menu items endpoint which instructs the conversion, or do the conversion itself serverside.

@getdave
Copy link
Contributor

getdave commented Dec 10, 2021

Let's create a follow up Issue so we don't forgot to explore this.

@manooweb this change is marked unstable so the implementation may change in the future (after WP 5.9). I recommend keeping an eye on developments in #35521.

@noisysocks noisysocks merged commit 51abd57 into trunk Dec 13, 2021
@noisysocks noisysocks deleted the add/navigation-blocks-post-processing-after-migration-from-menu-items branch December 13, 2021 00:36
@github-actions github-actions bot added this to the Gutenberg 12.2 milestone Dec 13, 2021
@noisysocks noisysocks removed the Backport to WP Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Dec 13, 2021
noisysocks pushed a commit that referenced this pull request Dec 13, 2021
…#36950)

* add navigation.menuItemsToBlocks filter to postprocess blocks created from menu items

* Add navigation_after_parse_blocks_from_menu_items filter

* Make the filter experimental

* Lint

* Update menu-items-to-blocks.js
@manooweb
Copy link
Contributor

@draganescu Yes! I understand too and prepare our release with the new unstable filter name.
@getdave I don't see any subject about this in the issue you linked. Did I miss something?
However I already subscribed to it to further follow the developments and I'm going to see if I can set up a test in our side to detect the future changes as soon as possible.

Thanks all for the reactivity and by backported this in the beta/RC releases 👍

@spacedmonkey
Copy link
Member

Is this ticket not being backported?

@adamziel adamziel added Backport to WP Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta and removed Backport to WP Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta labels Dec 13, 2021
@adamziel
Copy link
Contributor Author

I think it is here WordPress/wordpress-develop#2048

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Navigation Affects the Navigation Block
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants