From bbd7144f2471413240ca3a825465e12673e6d7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 4 May 2021 12:28:30 +0200 Subject: [PATCH 1/8] Typos --- lib/class-wp-rest-menus-controller.php | 2 +- .../src/components/link-control/use-search-handler.js | 2 +- packages/block-library/src/post-navigation-link/index.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/class-wp-rest-menus-controller.php b/lib/class-wp-rest-menus-controller.php index 84fc22952aa28e..18e2fbacc0012a 100644 --- a/lib/class-wp-rest-menus-controller.php +++ b/lib/class-wp-rest-menus-controller.php @@ -382,7 +382,7 @@ public function update_item( $request ) { $prepared_term = $this->prepare_item_for_database( $request ); - // Only update the term if we haz something to update. + // Only update the term if we have something to update. if ( ! empty( $prepared_term ) ) { $update = wp_update_nav_menu_object( $term->term_id, wp_slash( (array) $prepared_term ) ); diff --git a/packages/block-editor/src/components/link-control/use-search-handler.js b/packages/block-editor/src/components/link-control/use-search-handler.js index 37fc89951b9416..47d1f8580b9959 100644 --- a/packages/block-editor/src/components/link-control/use-search-handler.js +++ b/packages/block-editor/src/components/link-control/use-search-handler.js @@ -94,7 +94,7 @@ const handleEntitySearch = async ( return isURLLike( val ) || ! withCreateSuggestion ? results : results.concat( { - // the `id` prop is intentionally ommitted here because it + // the `id` prop is intentionally omitted here because it // is never exposed as part of the component's public API. // see: https://github.com/WordPress/gutenberg/pull/19775#discussion_r378931316. title: val, // must match the existing ``s text value diff --git a/packages/block-library/src/post-navigation-link/index.php b/packages/block-library/src/post-navigation-link/index.php index 0c3e294adb714d..57c5ae94c8bb51 100644 --- a/packages/block-library/src/post-navigation-link/index.php +++ b/packages/block-library/src/post-navigation-link/index.php @@ -18,7 +18,7 @@ function render_block_core_post_navigation_link( $attributes, $content ) { return ''; } - // Get the nagigation type to show the proper link. Available options are `next|previous`. + // Get the navigation type to show the proper link. Available options are `next|previous`. $navigation_type = isset( $attributes['type'] ) ? $attributes['type'] : 'next'; // Allow only `next` and `previous` in `$navigation_type`. if ( ! in_array( $navigation_type, array( 'next', 'previous' ), true ) ) { From 98ddf3018231fb0f384f5837e3b834a9367e79a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 4 May 2021 12:29:32 +0200 Subject: [PATCH 2/8] Add missing LinkControl prop in README --- .../block-editor/src/components/link-control/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/block-editor/src/components/link-control/README.md b/packages/block-editor/src/components/link-control/README.md index d143b7b5fcb3ed..6c54a04480c80c 100644 --- a/packages/block-editor/src/components/link-control/README.md +++ b/packages/block-editor/src/components/link-control/README.md @@ -105,6 +105,13 @@ Whether to present suggestions when typing the URL. Whether to present initial suggestions immediately. +### noDirectEntry + +- Type: `boolean` +- Required: No + +Whether to allow turning a URL-like search query directly into a link. + ### forceIsEditingLink - Type: `boolean` From 4bfdc36b3ab64f71a7a49ce51f7dfe8f46a61ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 4 May 2021 12:32:39 +0200 Subject: [PATCH 3/8] Add Navigation Link block variations for all item types added in customize_nav_menu_available_item_types --- .../src/components/link-control/README.md | 7 +++++ .../src/components/link-control/index.js | 2 ++ .../block-library/src/navigation-link/edit.js | 28 ++++++++++++++++++- .../src/navigation-link/index.php | 20 ++++++++++++- 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/link-control/README.md b/packages/block-editor/src/components/link-control/README.md index 6c54a04480c80c..968cc7683a31cd 100644 --- a/packages/block-editor/src/components/link-control/README.md +++ b/packages/block-editor/src/components/link-control/README.md @@ -105,6 +105,13 @@ Whether to present suggestions when typing the URL. Whether to present initial suggestions immediately. +### fetchSuggestions + +- Type: `Function` +- Required: No + +Custom search handler for suggestions. If specified, it's passed to `LinkControlSearchInput` which later passes it to `URLInput`. Refer to [`LinkControlSearchInput` docs below](#linkcontrolsearchinput) or `URLInput`'s README.md for more details about it. + ### noDirectEntry - Type: `boolean` diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index b9a63ceb71da3c..81e112b433f3e1 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -117,6 +117,7 @@ function LinkControl( { noURLSuggestion = false, createSuggestionButtonText, hasRichPreviews = false, + fetchSuggestions, } ) { if ( withCreateSuggestion === undefined && createSuggestion ) { withCreateSuggestion = true; @@ -217,6 +218,7 @@ function LinkControl( { allowDirectEntry={ ! noDirectEntry } showSuggestions={ showSuggestions } suggestionsQuery={ suggestionsQuery } + fetchSuggestions={ fetchSuggestions } withURLSuggestion={ ! noURLSuggestion } createSuggestionButtonText={ createSuggestionButtonText diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 641d95ec5c2e06..3f3d98e2614b28 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -228,6 +228,7 @@ export default function NavigationLinkEdit( { rel, title, kind, + suggestions = [], } = attributes; const link = { url, @@ -449,6 +450,22 @@ export default function NavigationLinkEdit( { missingText = __( 'Add a link' ); } + const fetchCustomItemTypeSuggestions = ( val ) => { + return Promise.resolve( + suggestions + .filter( + ( item ) => + val === '' || item.title.match( new RegExp( val, 'i' ) ) + ) + .map( ( item ) => ( { + id: item.id, + title: item.title, + url: item.url, + type: 'URL', + } ) ) + ); + }; + return ( @@ -569,7 +586,11 @@ export default function NavigationLinkEdit( { className="wp-block-navigation-link__inline-link-input" value={ link } showInitialSuggestions={ true } - withCreateSuggestion={ userCanCreate } + withCreateSuggestion={ + suggestions.length > 0 + ? false + : userCanCreate + } createSuggestion={ handleCreate } createSuggestionButtonText={ ( searchTerm ) => { let format; @@ -595,6 +616,11 @@ export default function NavigationLinkEdit( { type, kind ) } + fetchSuggestions={ + suggestions.length > 0 + ? fetchCustomItemTypeSuggestions + : null + } onChange={ ( updatedValue ) => updateNavigationLinkBlockAttributes( updatedValue, diff --git a/packages/block-library/src/navigation-link/index.php b/packages/block-library/src/navigation-link/index.php index a770c48d185e5f..25a4f1408d9461 100644 --- a/packages/block-library/src/navigation-link/index.php +++ b/packages/block-library/src/navigation-link/index.php @@ -322,11 +322,29 @@ function register_block_core_navigation_link() { } } + // Some plugins use the `customize_nav_menu_available_item_types` filter to + // add new menu item types. + $item_types = apply_filters( 'customize_nav_menu_available_item_types', array() ); + $filter_variations = array(); + if ( is_array( $item_types ) ) { + foreach ( $item_types as $item_type ) { + $filter_variations[] = array( + 'name' => $item_type['object'], + 'title' => $item_type['title'], + 'description' => '', + 'attributes' => array( + 'type' => $item_type['type'], + 'suggestions' => apply_filters( 'customize_nav_menu_available_items', array(), $item_type['type'], $item_type['object'] ), + ), + ); + } + } + register_block_type_from_metadata( __DIR__ . '/navigation-link', array( 'render_callback' => 'render_block_core_navigation_link', - 'variations' => array_merge( $built_ins, $variations ), + 'variations' => array_merge( $built_ins, $variations, $filter_variations ), ) ); } From 87af6d8b09d5b8f7c64df5890df7fa963c794c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Thu, 6 May 2021 11:34:42 +0200 Subject: [PATCH 4/8] Create endpoint to fetch menu custom items --- ...s-wp-rest-menu-custom-items-controller.php | 78 +++++++++++++++++++ lib/load.php | 3 + lib/rest-api.php | 9 +++ .../block-library/src/navigation-link/edit.js | 36 +++++---- .../src/navigation-link/index.php | 3 +- .../__experimental-fetch-menu-custom-items.js | 19 +++++ packages/core-data/src/fetch/index.js | 1 + packages/edit-navigation/src/index.js | 8 +- packages/edit-site/src/index.js | 7 +- packages/edit-widgets/src/index.js | 7 +- .../provider/use-block-editor-settings.js | 5 ++ 11 files changed, 157 insertions(+), 19 deletions(-) create mode 100644 lib/class-wp-rest-menu-custom-items-controller.php create mode 100644 packages/core-data/src/fetch/__experimental-fetch-menu-custom-items.js diff --git a/lib/class-wp-rest-menu-custom-items-controller.php b/lib/class-wp-rest-menu-custom-items-controller.php new file mode 100644 index 00000000000000..76ea7059fb916d --- /dev/null +++ b/lib/class-wp-rest-menu-custom-items-controller.php @@ -0,0 +1,78 @@ +namespace = '__experimental'; + $this->rest_base = 'menu-custom-items'; + } + + /** + * Registers the necessary REST API routes. + * + * @access public + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_menu_custom_items' ), + 'permission_callback' => array( $this, 'permissions_check' ), + 'args' => $this->get_collection_params(), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + } + + /** + * Checks if a given request has access to read menu items if they have access to edit them. + * + * @return true|WP_Error True if the request has read access, WP_Error object otherwise. + */ + public function permissions_check() { + $post_type = get_post_type_object( 'nav_menu_item' ); + if ( ! current_user_can( $post_type->cap->edit_posts ) ) { + return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to view menu items.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) ); + } + return true; + } + + /** + * Returns the menu items added via the + * `customize_nav_menu_available_item_types` filter. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Menu custom items, or WP_Error if type could not be found. + */ + public function get_menu_custom_items( $request ) { + $requested_type = $request->get_param( 'type' ); + $item_types = apply_filters( 'customize_nav_menu_available_item_types', array() ); + + if ( is_array( $item_types ) ) { + foreach ( $item_types as $item_type ) { + if ( $item_type['type'] === $requested_type ) { + return rest_ensure_response( + apply_filters( 'customize_nav_menu_available_items', array(), $item_type['type'], $item_type['object'], 0 ) + ); + } + } + } + + return new WP_Error( 'rest_invalid_menu_item_type', __( 'This item type could not be found.', 'gutenberg' ), array( 'status' => 404 ) ); + } +} diff --git a/lib/load.php b/lib/load.php index f81e7f3e8621cb..b6265039fa61f0 100644 --- a/lib/load.php +++ b/lib/load.php @@ -47,6 +47,9 @@ function gutenberg_is_experiment_enabled( $name ) { if ( ! class_exists( 'WP_REST_Menus_Controller' ) ) { require_once __DIR__ . '/class-wp-rest-menus-controller.php'; } + if ( ! class_exists( 'WP_REST_Menu_Custom_Items_Controller' ) ) { + require_once __DIR__ . '/class-wp-rest-menu-custom-items-controller.php'; + } if ( ! class_exists( 'WP_REST_Menu_Items_Controller' ) ) { require_once __DIR__ . '/class-wp-rest-menu-items-controller.php'; } diff --git a/lib/rest-api.php b/lib/rest-api.php index aa74c373f8b5b1..13e1061a99628e 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -49,6 +49,15 @@ function gutenberg_register_rest_customizer_nonces() { } add_action( 'rest_api_init', 'gutenberg_register_rest_customizer_nonces' ); +/** + * Registers the custom menu items REST API route. + */ +function gutenberg_register_rest_menu_custom_items() { + $nav_menu_location = new WP_REST_Menu_Custom_Items_Controller(); + $nav_menu_location->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_menu_custom_items' ); + /** * Registers the Sidebars & Widgets REST API routes. */ diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 3f3d98e2614b28..41919ace2d526e 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -228,7 +228,6 @@ export default function NavigationLinkEdit( { rel, title, kind, - suggestions = [], } = attributes; const link = { url, @@ -243,6 +242,16 @@ export default function NavigationLinkEdit( { const itemLabelPlaceholder = __( 'Add link…' ); const ref = useRef(); + const isCustomItemType = + ! [ + 'post', + 'page', + 'category', + 'tag', + 'post_format', + 'custom', + ].includes( type ) && ! [ 'taxonomy', 'post-type' ].includes( kind ); + const { isAtMaxNesting, isParentOfSelectedBlock, @@ -252,6 +261,7 @@ export default function NavigationLinkEdit( { numberOfDescendants, userCanCreatePages, userCanCreatePosts, + fetchMenuCustomItems, } = useSelect( ( select ) => { const { @@ -259,6 +269,7 @@ export default function NavigationLinkEdit( { hasSelectedInnerBlock, getSelectedBlockClientId, getBlockParentsByBlockName, + getSettings, } = select( blockEditorStore ); const selectedBlockId = getSelectedBlockClientId(); @@ -291,6 +302,8 @@ export default function NavigationLinkEdit( { 'create', 'posts' ), + fetchMenuCustomItems: getSettings() + .__experimentalFetchMenuCustomItems, }; }, [ clientId ] @@ -450,19 +463,16 @@ export default function NavigationLinkEdit( { missingText = __( 'Add a link' ); } - const fetchCustomItemTypeSuggestions = ( val ) => { - return Promise.resolve( - suggestions - .filter( - ( item ) => - val === '' || item.title.match( new RegExp( val, 'i' ) ) - ) - .map( ( item ) => ( { + const fetchCustomItemTypeSuggestions = ( search ) => { + return fetchMenuCustomItems( search, { type } ).then( + ( customItems ) => { + return customItems.map( ( item ) => ( { id: item.id, title: item.title, url: item.url, type: 'URL', - } ) ) + } ) ); + } ); }; @@ -587,9 +597,7 @@ export default function NavigationLinkEdit( { value={ link } showInitialSuggestions={ true } withCreateSuggestion={ - suggestions.length > 0 - ? false - : userCanCreate + isCustomItemType ? false : userCanCreate } createSuggestion={ handleCreate } createSuggestionButtonText={ ( searchTerm ) => { @@ -617,7 +625,7 @@ export default function NavigationLinkEdit( { kind ) } fetchSuggestions={ - suggestions.length > 0 + isCustomItemType ? fetchCustomItemTypeSuggestions : null } diff --git a/packages/block-library/src/navigation-link/index.php b/packages/block-library/src/navigation-link/index.php index 25a4f1408d9461..729d024e2dcd37 100644 --- a/packages/block-library/src/navigation-link/index.php +++ b/packages/block-library/src/navigation-link/index.php @@ -333,8 +333,7 @@ function register_block_core_navigation_link() { 'title' => $item_type['title'], 'description' => '', 'attributes' => array( - 'type' => $item_type['type'], - 'suggestions' => apply_filters( 'customize_nav_menu_available_items', array(), $item_type['type'], $item_type['object'] ), + 'type' => $item_type['type'], ), ); } diff --git a/packages/core-data/src/fetch/__experimental-fetch-menu-custom-items.js b/packages/core-data/src/fetch/__experimental-fetch-menu-custom-items.js new file mode 100644 index 00000000000000..41bc31fae2099d --- /dev/null +++ b/packages/core-data/src/fetch/__experimental-fetch-menu-custom-items.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; + +const fetchMenuCustomItems = async ( search, { type } ) => { + const path = addQueryArgs( '/__experimental/menu-custom-items', { type } ); + return apiFetch( { + path, + } ).then( ( results ) => { + return results.filter( + ( result ) => + search === '' || result.title.match( new RegExp( search, 'i' ) ) + ); + } ); +}; + +export default fetchMenuCustomItems; diff --git a/packages/core-data/src/fetch/index.js b/packages/core-data/src/fetch/index.js index 805dab0d63bfdc..c8596262d201dc 100644 --- a/packages/core-data/src/fetch/index.js +++ b/packages/core-data/src/fetch/index.js @@ -1,2 +1,3 @@ export { default as __experimentalFetchLinkSuggestions } from './__experimental-fetch-link-suggestions'; export { default as __experimentalFetchRemoteUrlData } from './__experimental-fetch-remote-url-data'; +export { default as __experimentalFetchMenuCustomItems } from './__experimental-fetch-menu-custom-items'; diff --git a/packages/edit-navigation/src/index.js b/packages/edit-navigation/src/index.js index 54ecb2df73c889..a309f26ff8eb9b 100644 --- a/packages/edit-navigation/src/index.js +++ b/packages/edit-navigation/src/index.js @@ -6,7 +6,10 @@ import { __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; import { render } from '@wordpress/element'; -import { __experimentalFetchLinkSuggestions as fetchLinkSuggestions } from '@wordpress/core-data'; +import { + __experimentalFetchLinkSuggestions as fetchLinkSuggestions, + __experimentalFetchMenuCustomItems as fetchMenuCustomItems, +} from '@wordpress/core-data'; /** * Internal dependencies @@ -30,6 +33,9 @@ export function initialize( id, settings ) { settings.__experimentalFetchLinkSuggestions = ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ); + settings.__experimentalFetchMenuCustomItems = ( search, searchOptions ) => + fetchMenuCustomItems( search, searchOptions, settings ); + render( , document.getElementById( id ) diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index fe42ad69af26c6..a1fa917cd7f4f5 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -6,7 +6,10 @@ import { __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; import { render } from '@wordpress/element'; -import { __experimentalFetchLinkSuggestions as fetchLinkSuggestions } from '@wordpress/core-data'; +import { + __experimentalFetchLinkSuggestions as fetchLinkSuggestions, + __experimentalFetchMenuCustomItems as fetchMenuCustomItems, +} from '@wordpress/core-data'; /** * Internal dependencies @@ -25,6 +28,8 @@ import Editor from './components/editor'; export function initialize( id, settings ) { settings.__experimentalFetchLinkSuggestions = ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ); + settings.__experimentalFetchMenuCustomItems = ( search, searchOptions ) => + fetchMenuCustomItems( search, searchOptions, settings ); settings.__experimentalSpotlightEntityBlocks = [ 'core/template-part' ]; registerCoreBlocks(); diff --git a/packages/edit-widgets/src/index.js b/packages/edit-widgets/src/index.js index b69edb7b2fc132..a92dc78f158915 100644 --- a/packages/edit-widgets/src/index.js +++ b/packages/edit-widgets/src/index.js @@ -11,7 +11,10 @@ import { __experimentalGetCoreBlocks, __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; -import { __experimentalFetchLinkSuggestions as fetchLinkSuggestions } from '@wordpress/core-data'; +import { + __experimentalFetchLinkSuggestions as fetchLinkSuggestions, + __experimentalFetchMenuCustomItems as fetchMenuCustomItems, +} from '@wordpress/core-data'; import { registerLegacyWidgetBlock, registerLegacyWidgetVariations, @@ -44,6 +47,8 @@ export function initialize( id, settings ) { registerBlock( widgetArea ); settings.__experimentalFetchLinkSuggestions = ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ); + settings.__experimentalFetchMenuCustomItems = ( search, searchOptions ) => + fetchMenuCustomItems( search, searchOptions, settings ); render( , document.getElementById( id ) diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index fb94a7f07934ca..8155e055f23e0e 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -12,6 +12,7 @@ import { store as coreStore, __experimentalFetchLinkSuggestions as fetchLinkSuggestions, __experimentalFetchRemoteUrlData as fetchRemoteUrlData, + __experimentalFetchMenuCustomItems as fetchMenuCustomItems, } from '@wordpress/core-data'; import { getAuthority, @@ -171,6 +172,10 @@ function useBlockEditorSettings( settings, hasTemplate ) { __experimentalFetchLinkSuggestions: ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ), __experimentalFetchRichUrlData: fetchRichUrlData, + __experimentalFetchRemoteUrlData: ( url ) => + fetchRemoteUrlData( url ), + __experimentalFetchMenuCustomItems: ( search, searchOptions ) => + fetchMenuCustomItems( search, searchOptions, settings ), __experimentalCanUserUseUnfilteredHTML: canUseUnfilteredHTML, __experimentalUndo: undo, outlineMode: hasTemplate, From 9490e72b7c424c02d2f71f02aebf3728edb01c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Fri, 7 May 2021 11:17:12 +0200 Subject: [PATCH 5/8] Improve isCustomItemType check --- packages/block-library/src/navigation-link/edit.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 41919ace2d526e..0460f21a02f2bc 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -243,6 +243,7 @@ export default function NavigationLinkEdit( { const ref = useRef(); const isCustomItemType = + typeof type !== 'undefined' && ! [ 'post', 'page', @@ -250,7 +251,8 @@ export default function NavigationLinkEdit( { 'tag', 'post_format', 'custom', - ].includes( type ) && ! [ 'taxonomy', 'post-type' ].includes( kind ); + ].includes( type ) && + ! [ 'taxonomy', 'post-type' ].includes( kind ); const { isAtMaxNesting, From 89aa72242762ca7bc54d5e5cc379d379852e432b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 8 Jun 2021 11:13:52 +0200 Subject: [PATCH 6/8] Bundle fetching of custom item types into fetchLinkSuggestions --- .../src/components/link-control/README.md | 7 ---- .../src/components/link-control/index.js | 2 - .../components/link-control/search-input.js | 3 +- .../block-library/src/navigation-link/edit.js | 40 +------------------ .../__experimental-fetch-link-suggestions.js | 27 +++++++++++++ .../__experimental-fetch-menu-custom-items.js | 19 --------- packages/core-data/src/fetch/index.js | 1 - packages/edit-navigation/src/index.js | 8 +--- packages/edit-site/src/index.js | 7 +--- packages/edit-widgets/src/index.js | 7 +--- .../provider/use-block-editor-settings.js | 5 --- 11 files changed, 33 insertions(+), 93 deletions(-) delete mode 100644 packages/core-data/src/fetch/__experimental-fetch-menu-custom-items.js diff --git a/packages/block-editor/src/components/link-control/README.md b/packages/block-editor/src/components/link-control/README.md index 968cc7683a31cd..6c54a04480c80c 100644 --- a/packages/block-editor/src/components/link-control/README.md +++ b/packages/block-editor/src/components/link-control/README.md @@ -105,13 +105,6 @@ Whether to present suggestions when typing the URL. Whether to present initial suggestions immediately. -### fetchSuggestions - -- Type: `Function` -- Required: No - -Custom search handler for suggestions. If specified, it's passed to `LinkControlSearchInput` which later passes it to `URLInput`. Refer to [`LinkControlSearchInput` docs below](#linkcontrolsearchinput) or `URLInput`'s README.md for more details about it. - ### noDirectEntry - Type: `boolean` diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index 81e112b433f3e1..b9a63ceb71da3c 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -117,7 +117,6 @@ function LinkControl( { noURLSuggestion = false, createSuggestionButtonText, hasRichPreviews = false, - fetchSuggestions, } ) { if ( withCreateSuggestion === undefined && createSuggestion ) { withCreateSuggestion = true; @@ -218,7 +217,6 @@ function LinkControl( { allowDirectEntry={ ! noDirectEntry } showSuggestions={ showSuggestions } suggestionsQuery={ suggestionsQuery } - fetchSuggestions={ fetchSuggestions } withURLSuggestion={ ! noURLSuggestion } createSuggestionButtonText={ createSuggestionButtonText diff --git a/packages/block-editor/src/components/link-control/search-input.js b/packages/block-editor/src/components/link-control/search-input.js index aea9a903d9f52c..8599a8b7202cd4 100644 --- a/packages/block-editor/src/components/link-control/search-input.js +++ b/packages/block-editor/src/components/link-control/search-input.js @@ -35,7 +35,6 @@ const LinkControlSearchInput = forwardRef( renderSuggestions = ( props ) => ( ), - fetchSuggestions = null, allowDirectEntry = true, showInitialSuggestions = false, suggestionsQuery = {}, @@ -51,7 +50,7 @@ const LinkControlSearchInput = forwardRef( withURLSuggestion ); const searchHandler = showSuggestions - ? fetchSuggestions || genericSearchHandler + ? genericSearchHandler : noopSearchHandler; const instanceId = useInstanceId( LinkControlSearchInput ); diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 0460f21a02f2bc..e4f43e4017092a 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -129,7 +129,7 @@ function getSuggestionsQuery( type, kind ) { if ( kind === 'post-type' ) { return { type: 'post', subtype: type }; } - return {}; + return { type }; } } @@ -242,18 +242,6 @@ export default function NavigationLinkEdit( { const itemLabelPlaceholder = __( 'Add link…' ); const ref = useRef(); - const isCustomItemType = - typeof type !== 'undefined' && - ! [ - 'post', - 'page', - 'category', - 'tag', - 'post_format', - 'custom', - ].includes( type ) && - ! [ 'taxonomy', 'post-type' ].includes( kind ); - const { isAtMaxNesting, isParentOfSelectedBlock, @@ -263,7 +251,6 @@ export default function NavigationLinkEdit( { numberOfDescendants, userCanCreatePages, userCanCreatePosts, - fetchMenuCustomItems, } = useSelect( ( select ) => { const { @@ -271,7 +258,6 @@ export default function NavigationLinkEdit( { hasSelectedInnerBlock, getSelectedBlockClientId, getBlockParentsByBlockName, - getSettings, } = select( blockEditorStore ); const selectedBlockId = getSelectedBlockClientId(); @@ -304,8 +290,6 @@ export default function NavigationLinkEdit( { 'create', 'posts' ), - fetchMenuCustomItems: getSettings() - .__experimentalFetchMenuCustomItems, }; }, [ clientId ] @@ -465,19 +449,6 @@ export default function NavigationLinkEdit( { missingText = __( 'Add a link' ); } - const fetchCustomItemTypeSuggestions = ( search ) => { - return fetchMenuCustomItems( search, { type } ).then( - ( customItems ) => { - return customItems.map( ( item ) => ( { - id: item.id, - title: item.title, - url: item.url, - type: 'URL', - } ) ); - } - ); - }; - return ( @@ -598,9 +569,7 @@ export default function NavigationLinkEdit( { className="wp-block-navigation-link__inline-link-input" value={ link } showInitialSuggestions={ true } - withCreateSuggestion={ - isCustomItemType ? false : userCanCreate - } + withCreateSuggestion={ userCanCreate } createSuggestion={ handleCreate } createSuggestionButtonText={ ( searchTerm ) => { let format; @@ -626,11 +595,6 @@ export default function NavigationLinkEdit( { type, kind ) } - fetchSuggestions={ - isCustomItemType - ? fetchCustomItemTypeSuggestions - : null - } onChange={ ( updatedValue ) => updateNavigationLinkBlockAttributes( updatedValue, diff --git a/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js index add566050d4b31..f57134fff5d51f 100644 --- a/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js +++ b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js @@ -153,6 +153,33 @@ const fetchLinkSuggestions = async ( ); } + if ( + type && + type !== 'post' && + type !== 'term' && + type !== 'post-format' + ) { + queries.push( + apiFetch( { + path: addQueryArgs( '/__experimental/menu-custom-items', { + type, + } ), + } ).then( ( results ) => { + const customItems = results.filter( + ( result ) => + search === '' || + result.title.match( new RegExp( search, 'i' ) ) + ); + return customItems.map( ( item ) => ( { + id: item.id, + title: item.title, + url: item.url, + type: 'URL', + } ) ); + } ) + ); + } + return Promise.all( queries ).then( ( results ) => { return results .reduce( diff --git a/packages/core-data/src/fetch/__experimental-fetch-menu-custom-items.js b/packages/core-data/src/fetch/__experimental-fetch-menu-custom-items.js deleted file mode 100644 index 41bc31fae2099d..00000000000000 --- a/packages/core-data/src/fetch/__experimental-fetch-menu-custom-items.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * WordPress dependencies - */ -import apiFetch from '@wordpress/api-fetch'; -import { addQueryArgs } from '@wordpress/url'; - -const fetchMenuCustomItems = async ( search, { type } ) => { - const path = addQueryArgs( '/__experimental/menu-custom-items', { type } ); - return apiFetch( { - path, - } ).then( ( results ) => { - return results.filter( - ( result ) => - search === '' || result.title.match( new RegExp( search, 'i' ) ) - ); - } ); -}; - -export default fetchMenuCustomItems; diff --git a/packages/core-data/src/fetch/index.js b/packages/core-data/src/fetch/index.js index c8596262d201dc..805dab0d63bfdc 100644 --- a/packages/core-data/src/fetch/index.js +++ b/packages/core-data/src/fetch/index.js @@ -1,3 +1,2 @@ export { default as __experimentalFetchLinkSuggestions } from './__experimental-fetch-link-suggestions'; export { default as __experimentalFetchRemoteUrlData } from './__experimental-fetch-remote-url-data'; -export { default as __experimentalFetchMenuCustomItems } from './__experimental-fetch-menu-custom-items'; diff --git a/packages/edit-navigation/src/index.js b/packages/edit-navigation/src/index.js index a309f26ff8eb9b..54ecb2df73c889 100644 --- a/packages/edit-navigation/src/index.js +++ b/packages/edit-navigation/src/index.js @@ -6,10 +6,7 @@ import { __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; import { render } from '@wordpress/element'; -import { - __experimentalFetchLinkSuggestions as fetchLinkSuggestions, - __experimentalFetchMenuCustomItems as fetchMenuCustomItems, -} from '@wordpress/core-data'; +import { __experimentalFetchLinkSuggestions as fetchLinkSuggestions } from '@wordpress/core-data'; /** * Internal dependencies @@ -33,9 +30,6 @@ export function initialize( id, settings ) { settings.__experimentalFetchLinkSuggestions = ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ); - settings.__experimentalFetchMenuCustomItems = ( search, searchOptions ) => - fetchMenuCustomItems( search, searchOptions, settings ); - render( , document.getElementById( id ) diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index a1fa917cd7f4f5..fe42ad69af26c6 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -6,10 +6,7 @@ import { __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; import { render } from '@wordpress/element'; -import { - __experimentalFetchLinkSuggestions as fetchLinkSuggestions, - __experimentalFetchMenuCustomItems as fetchMenuCustomItems, -} from '@wordpress/core-data'; +import { __experimentalFetchLinkSuggestions as fetchLinkSuggestions } from '@wordpress/core-data'; /** * Internal dependencies @@ -28,8 +25,6 @@ import Editor from './components/editor'; export function initialize( id, settings ) { settings.__experimentalFetchLinkSuggestions = ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ); - settings.__experimentalFetchMenuCustomItems = ( search, searchOptions ) => - fetchMenuCustomItems( search, searchOptions, settings ); settings.__experimentalSpotlightEntityBlocks = [ 'core/template-part' ]; registerCoreBlocks(); diff --git a/packages/edit-widgets/src/index.js b/packages/edit-widgets/src/index.js index a92dc78f158915..b69edb7b2fc132 100644 --- a/packages/edit-widgets/src/index.js +++ b/packages/edit-widgets/src/index.js @@ -11,10 +11,7 @@ import { __experimentalGetCoreBlocks, __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; -import { - __experimentalFetchLinkSuggestions as fetchLinkSuggestions, - __experimentalFetchMenuCustomItems as fetchMenuCustomItems, -} from '@wordpress/core-data'; +import { __experimentalFetchLinkSuggestions as fetchLinkSuggestions } from '@wordpress/core-data'; import { registerLegacyWidgetBlock, registerLegacyWidgetVariations, @@ -47,8 +44,6 @@ export function initialize( id, settings ) { registerBlock( widgetArea ); settings.__experimentalFetchLinkSuggestions = ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ); - settings.__experimentalFetchMenuCustomItems = ( search, searchOptions ) => - fetchMenuCustomItems( search, searchOptions, settings ); render( , document.getElementById( id ) diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 8155e055f23e0e..fb94a7f07934ca 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -12,7 +12,6 @@ import { store as coreStore, __experimentalFetchLinkSuggestions as fetchLinkSuggestions, __experimentalFetchRemoteUrlData as fetchRemoteUrlData, - __experimentalFetchMenuCustomItems as fetchMenuCustomItems, } from '@wordpress/core-data'; import { getAuthority, @@ -172,10 +171,6 @@ function useBlockEditorSettings( settings, hasTemplate ) { __experimentalFetchLinkSuggestions: ( search, searchOptions ) => fetchLinkSuggestions( search, searchOptions, settings ), __experimentalFetchRichUrlData: fetchRichUrlData, - __experimentalFetchRemoteUrlData: ( url ) => - fetchRemoteUrlData( url ), - __experimentalFetchMenuCustomItems: ( search, searchOptions ) => - fetchMenuCustomItems( search, searchOptions, settings ), __experimentalCanUserUseUnfilteredHTML: canUseUnfilteredHTML, __experimentalUndo: undo, outlineMode: hasTemplate, From 08da2108503a3129cc677605923fafbec9d436e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Tue, 8 Jun 2021 13:45:01 +0200 Subject: [PATCH 7/8] Add get_item_schema and prepare_item_for_response methods --- ...s-wp-rest-menu-custom-items-controller.php | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/lib/class-wp-rest-menu-custom-items-controller.php b/lib/class-wp-rest-menu-custom-items-controller.php index 76ea7059fb916d..3c2cf45681a850 100644 --- a/lib/class-wp-rest-menu-custom-items-controller.php +++ b/lib/class-wp-rest-menu-custom-items-controller.php @@ -52,6 +52,44 @@ public function permissions_check() { return true; } + /** + * Retrieves the menu custom items' schema, conforming to JSON Schema. + * + * @return array Item schema data. + */ + public function get_item_schema() { + $this->schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'menu-custom-items', + 'type' => 'object', + 'properties' => array( + + 'id' => array( + 'description' => __( 'Unique identifier for the menu item.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), + ), + 'title' => array( + 'description' => __( 'Human-readable name identifying the menu item.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), + ), + 'type_label' => array( + 'description' => __( 'Type of link.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), + ), + 'url' => array( + 'description' => __( 'URL of the link.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), + ), + ), + ); + + return $this->add_additional_fields_schema( $this->schema ); + } + /** * Returns the menu items added via the * `customize_nav_menu_available_item_types` filter. @@ -66,13 +104,25 @@ public function get_menu_custom_items( $request ) { if ( is_array( $item_types ) ) { foreach ( $item_types as $item_type ) { if ( $item_type['type'] === $requested_type ) { - return rest_ensure_response( - apply_filters( 'customize_nav_menu_available_items', array(), $item_type['type'], $item_type['object'], 0 ) - ); + return $this->prepare_item_for_response( $item_type, $request ); } } } return new WP_Error( 'rest_invalid_menu_item_type', __( 'This item type could not be found.', 'gutenberg' ), array( 'status' => 404 ) ); } + + /** + * Prepares a menu list of items for serialization. + * + * @param stdClass $item_type Item type data. + * @param WP_REST_Request $request Full details about the request. + * + * @return WP_REST_Response List of menu items. + */ + public function prepare_item_for_response( $item_type, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + return rest_ensure_response( + apply_filters( 'customize_nav_menu_available_items', array(), $item_type['type'], $item_type['object'], 0 ) + ); + } } From 5c34f3bd8d84820e9e0a4bb9508f1bffd2d32184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Thu, 17 Jun 2021 14:56:03 +0200 Subject: [PATCH 8/8] Use stringToLower instead of RegExp to find endpoint matches --- .../src/fetch/__experimental-fetch-link-suggestions.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js index f57134fff5d51f..1aa3b04923186e 100644 --- a/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js +++ b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js @@ -168,7 +168,9 @@ const fetchLinkSuggestions = async ( const customItems = results.filter( ( result ) => search === '' || - result.title.match( new RegExp( search, 'i' ) ) + result.title + .toLowerCase() + .includes( search.toLowerCase() ) ); return customItems.map( ( item ) => ( { id: item.id,