From aedb880662cdb7ba9fc27246092cd9c3565cbb43 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 21 Feb 2024 10:08:44 +0100 Subject: [PATCH] Initial effort --- lib/rest-api.php | 25 +++++++++++++ .../components/link-control/search-item.js | 23 ++++++------ .../src/navigation-link/index.php | 22 ++++++++++++ .../src/navigation-link/link-ui.js | 3 ++ .../__experimental-fetch-link-suggestions.ts | 36 ++++++++++++++++++- .../__experimental-fetch-link-suggestions.js | 19 +++++++++- 6 files changed, 116 insertions(+), 12 deletions(-) diff --git a/lib/rest-api.php b/lib/rest-api.php index 7570bb19737233..c4365ac392cb00 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -37,3 +37,28 @@ function gutenberg_register_edit_site_export_controller_endpoints() { $edit_site_export_controller->register_routes(); } add_action( 'rest_api_init', 'gutenberg_register_edit_site_export_controller_endpoints' ); + + +function gutenberg_register_archive_link_field() { + register_rest_field( + 'type', + 'archive_link', + array( + 'get_callback' => function( $post_object ) { + if ( isset( $post_object['has_archive'] ) && $post_object['has_archive'] ) { + $post_type = $post_object['slug']; + $archive_link = get_post_type_archive_link( $post_type ); + return $archive_link ? $archive_link : ''; + } + return ''; + }, + 'update_callback' => null, + 'schema' => array( + 'description' => __( 'Link to the post type archive page', 'default' ), + 'type' => 'string', + 'context' => array( 'view', 'edit' ), + ), + ) + ); +} +add_action( 'rest_api_init', 'gutenberg_register_archive_link_field' ); diff --git a/packages/block-editor/src/components/link-control/search-item.js b/packages/block-editor/src/components/link-control/search-item.js index fa8d1540b3daed..a790e420484719 100644 --- a/packages/block-editor/src/components/link-control/search-item.js +++ b/packages/block-editor/src/components/link-control/search-item.js @@ -13,6 +13,7 @@ import { file, home, verse, + customPostType, } from '@wordpress/icons'; import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; import { safeDecodeURI, filterURLForDisplay, getPath } from '@wordpress/url'; @@ -41,18 +42,16 @@ function SearchItemIcon( { isURL, suggestion } ) { icon = verse; } } + } else { + icon = customPostType; } - if ( icon ) { - return ( - - ); - } - - return null; + return ( + + ); } /** @@ -155,6 +154,10 @@ function getVisualTypeName( suggestion ) { return 'blog home'; } + if ( suggestion.kind === 'post-type-archive' ) { + return 'archive'; + } + // Rename 'post_tag' to 'tag'. Ideally, the API would return the localised CPT or taxonomy label. return suggestion.type === 'post_tag' ? 'tag' : suggestion.type; } diff --git a/packages/block-library/src/navigation-link/index.php b/packages/block-library/src/navigation-link/index.php index 5653e04fca88a3..8f45fc2a77f56a 100644 --- a/packages/block-library/src/navigation-link/index.php +++ b/packages/block-library/src/navigation-link/index.php @@ -371,6 +371,7 @@ function block_core_navigation_link_filter_variations( $variations, $block_type * @return array */ function block_core_navigation_link_build_variations() { + $post_types = get_post_types( array( 'show_in_nav_menus' => true ), 'objects' ); $taxonomies = get_taxonomies( array( 'show_in_nav_menus' => true ), 'objects' ); @@ -392,6 +393,27 @@ function block_core_navigation_link_build_variations() { $variations[] = $variation; } } + + // If any of the post types have `has_archive` set to true then add a post-type-archive variation. + $has_archive = array_filter( + $post_types, + function ( $post_type ) { + return $post_type->has_archive; + } + ); + + if ( $has_archive ) { + $variation = array( + 'name' => 'post-type-archive', + 'title' => __( 'Post Type Archive Link' ), + 'description' => __( 'A link to a post type archive' ), + 'attributes' => array( + 'type' => 'all', + 'kind' => 'post-type-archive', + ), + ); + $variations[] = $variation; + } } if ( $taxonomies ) { foreach ( $taxonomies as $taxonomy ) { diff --git a/packages/block-library/src/navigation-link/link-ui.js b/packages/block-library/src/navigation-link/link-ui.js index ee238c71ed28e0..cfcc1dd50af815 100644 --- a/packages/block-library/src/navigation-link/link-ui.js +++ b/packages/block-library/src/navigation-link/link-ui.js @@ -63,6 +63,9 @@ export function getSuggestionsQuery( type, kind ) { if ( kind === 'taxonomy' ) { return { type: 'term', subtype: type }; } + if ( kind === 'post-type-archive' ) { + return { type: 'post-type-archive' }; + } if ( kind === 'post-type' ) { return { type: 'post', subtype: type }; } diff --git a/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.ts b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.ts index e1a166ee272dbe..38791d11fc56d5 100644 --- a/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.ts +++ b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.ts @@ -21,7 +21,7 @@ export type SearchOptions = { /** * Filters by search type. */ - type?: 'attachment' | 'post' | 'term' | 'post-format'; + type?: 'attachment' | 'post' | 'term' | 'post-format' | 'post-type-archive'; /** * Slug of the post-type or taxonomy. */ @@ -58,6 +58,12 @@ type MediaAPIResult = { type: string; }; +type PostTypesAPIResult = { + slug: string; + name: string; + archive_link: string; +}; + export type SearchResult = { /** * Post or term id. @@ -240,6 +246,34 @@ export default async function fetchLinkSuggestions( ); } + if ( ! type || type === 'post-type-archive' ) { + queries.push( + apiFetch< PostTypesAPIResult[] >( { + path: addQueryArgs( '/wp/v2/types', { + page, + per_page: perPage, + } ), + } ) + .then( ( results ) => { + const resultValues = Object.values( results ); + return resultValues + .filter( ( result ) => !! result.archive_link ) // Filter out results with falsy archive_link, including empty strings + .map( ( result, index ) => { + return { + id: index + 1, // avoid results being filtered due to falsy id + url: result.archive_link, + title: + decodeEntities( result.name || '' ) || + __( '(no title)' ), + type: result.slug, + kind: 'post-type-archive', + }; + } ); + } ) + .catch( () => [] ) // Fail by returning no results. + ); + } + const responses = await Promise.all( queries ); let results = responses.flat(); diff --git a/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js b/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js index 6878c74332c3d7..4612a01537cdf5 100644 --- a/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js +++ b/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js @@ -86,6 +86,16 @@ jest.mock( '@wordpress/api-fetch', () => 'http://localhost:8888/wp-content/uploads/2022/03/test-pdf.pdf', }, ] ); + case '/wp/v2/search?search=&per_page=20&type=post-type-archive': + return Promise.resolve( [ + { + id: 'books', + title: 'All Books', + url: 'http://wordpress.local/books/', + type: 'books-archive', + kind: 'post-type-archive', + }, + ] ); default: return Promise.resolve( [ { @@ -187,7 +197,7 @@ describe( 'fetchLinkSuggestions', () => { ); } ); - it( 'returns suggestions from post, term, post-format and media', () => { + it( 'returns suggestions from post, term, post-format, media and post-type-archive', () => { return fetchLinkSuggestions( '', {} ).then( ( suggestions ) => expect( suggestions ).toEqual( [ { @@ -232,6 +242,13 @@ describe( 'fetchLinkSuggestions', () => { type: 'attachment', kind: 'media', }, + { + id: 'books', + title: 'All Books', + url: 'http://wordpress.local/books/', + type: 'books-archive', + kind: 'post-type-archive', + }, ] ) ); } );