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

Adds suggestions for categories and formats to LinkControl #22600

Merged
merged 24 commits into from
Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6f599da
adds suggestions for categories and formats
draganescu May 25, 2020
5fe7617
adds concurent search for suggestions
draganescu May 27, 2020
6ac0852
generalizes the category search handler to all terms
draganescu Jun 16, 2020
f23fc76
lint
draganescu Jun 16, 2020
2f22ea9
adds links to terms search results
draganescu Jun 18, 2020
a46af4e
adds default listing for empty searches and a stup test class
draganescu Jun 22, 2020
3f89946
shows empty terms in results for search and tests pass
draganescu Jun 22, 2020
07dd970
REST API: Add pagination to term search handler
noisysocks Aug 21, 2020
6082588
Navigation: Remove unnecessary async
noisysocks Aug 21, 2020
5a6edbb
REST API: Fix doc comments
noisysocks Aug 21, 2020
0e572f9
REST API: Use null instead of empty string
noisysocks Aug 21, 2020
1d6e65a
REST API: Add pagination to page format search handler
noisysocks Aug 21, 2020
e64499b
REST API: Fix term search handler tests
noisysocks Aug 21, 2020
c51cebd
REST API: Add post format search handler tests
noisysocks Aug 21, 2020
2f59617
REST API: Only show term subtypes which are REST API enabled
noisysocks Aug 25, 2020
fa053dd
REST API: Use WP_Term_Query's 'search' param
noisysocks Aug 25, 2020
f6c3618
REST API: Use wp_count_terms() instead of WP_Term_Query
noisysocks Aug 25, 2020
d09da71
REST API: Set fields=all when fetching terms so that they're cached
noisysocks Aug 25, 2020
4457b4a
REST API: Use rest_get_route_for_term() instead of custom method
noisysocks Aug 25, 2020
fc786ca
REST API: Pass forwards-compatible array to rest_post_format_search_q…
noisysocks Aug 25, 2020
26e1cb0
REST API: Improve stability of WP_REST_Post_Format_Search_Handler tests
noisysocks Aug 25, 2020
7408a1b
REST API: Add test for when no term matches search query
noisysocks Aug 25, 2020
d44950b
Navigation: Don't call /v2/search?type=post-format if theme does not …
noisysocks Aug 25, 2020
ef0042b
REST API: Fix post format handler tests
noisysocks Aug 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions lib/class-wp-rest-post-format-search-handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php
/**
* REST API: WP_REST_Post_Format_Search_Handler class
*
* @package Gutenberg
*/

/**
* Core class representing a search handler for post formats in the REST API.
*
* @see WP_REST_Search_Handler
*/
class WP_REST_Post_Format_Search_Handler extends WP_REST_Search_Handler {

/**
* Constructor.
*/
public function __construct() {
$this->type = 'post-format';
}

/**
* Searches the object type content for a given search request.
*
* @param WP_REST_Request $request Full REST request.
* @return array Associative array containing an `WP_REST_Search_Handler::RESULT_IDS` containing
* an array of found IDs and `WP_REST_Search_Handler::RESULT_TOTAL` containing the
* total count for the matching search results.
*/
public function search_items( WP_REST_Request $request ) {
$format_strings = get_post_format_strings();
TimothyBJacobs marked this conversation as resolved.
Show resolved Hide resolved
$format_slugs = array_keys( $format_strings );

$query_args = array();

if ( ! empty( $request['search'] ) ) {
$query_args['search'] = $request['search'];
}

/**
* Filters the query arguments for a search request.
*
* Enables adding extra arguments or setting defaults for a post format search request.
*
* @param array $query_args Key value array of query var to query value.
* @param WP_REST_Request $request The request used.
*/
$query_args = apply_filters( 'rest_post_format_search_query', $query_args, $request );

$found_ids = array();
foreach ( $format_slugs as $index => $format_slug ) {
if ( ! empty( $query_args['search'] ) ) {
$format_string = get_post_format_string( $format_slug );
$format_slug_match = stripos( $format_slug, $query_args['search'] ) !== false;
$format_string_match = stripos( $format_string, $query_args['search'] ) !== false;
if ( ! $format_slug_match && ! $format_string_match ) {
continue;
}
}

$format_link = get_post_format_link( $format_slug );
if ( $format_link ) {
// Formats don't have an ID, so fake one using the array index.
$found_ids[] = $index + 1;
noisysocks marked this conversation as resolved.
Show resolved Hide resolved
}
}

$page = (int) $request['page'];
$per_page = (int) $request['per_page'];

return array(
self::RESULT_IDS => array_slice( $found_ids, ( $page - 1 ) * $per_page, $per_page ),
self::RESULT_TOTAL => count( $found_ids ),
noisysocks marked this conversation as resolved.
Show resolved Hide resolved
);
}

/**
* Prepares the search result for a given ID.
*
* @param int $id Item ID.
* @param array $fields Fields to include for the item.
* @return array Associative array containing all fields for the item.
*/
public function prepare_item( $id, array $fields ) {
$format_strings = get_post_format_strings();
$format_slugs = array_keys( $format_strings );
$format_slug = $format_slugs[ $id - 1 ];

$data = array();

if ( in_array( WP_REST_Search_Controller::PROP_ID, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_ID ] = $id;
}

if ( in_array( WP_REST_Search_Controller::PROP_TITLE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_TITLE ] = get_post_format_string( $format_slug );
}

if ( in_array( WP_REST_Search_Controller::PROP_URL, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_URL ] = get_post_format_link( $format_slug );
}

if ( in_array( WP_REST_Search_Controller::PROP_TYPE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_TYPE ] = $this->type;
}

return $data;
}

/**
* Prepares links for the search result.
*
* @param string $id Item ID.
* @return array Links for the given item.
*/
public function prepare_item_links( $id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
return array();
noisysocks marked this conversation as resolved.
Show resolved Hide resolved
}

}
142 changes: 142 additions & 0 deletions lib/class-wp-rest-term-search-handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php
/**
* REST API: WP_REST_Terms_Search_Handler class
*
* @package Gutenberg
*/

/**
* Core class representing a search handler for term in the REST API.
*
* @see WP_REST_Search_Handler
*/
class WP_REST_Term_Search_Handler extends WP_REST_Search_Handler {

/**
* Constructor.
*/
public function __construct() {
$this->type = 'term';

$this->subtypes = array_values(
get_taxonomies(
array(
'public' => true,
'show_in_rest' => true,
),
'names'
)
);
}

/**
* Searches the object type content for a given search request.
*
* @param WP_REST_Request $request Full REST request.
* @return array Associative array containing an `WP_REST_Search_Handler::RESULT_IDS` containing
* an array of found IDs and `WP_REST_Search_Handler::RESULT_TOTAL` containing the
* total count for the matching search results.
*/
public function search_items( WP_REST_Request $request ) {
$taxonomies = $request[ WP_REST_Search_Controller::PROP_SUBTYPE ];
if ( in_array( WP_REST_Search_Controller::TYPE_ANY, $taxonomies, true ) ) {
$taxonomies = $this->subtypes;
}

$page = (int) $request['page'];
$per_page = (int) $request['per_page'];

$query_args = array(
'taxonomy' => $taxonomies,
'hide_empty' => false,
'offset' => ( $page - 1 ) * $per_page,
'number' => $per_page,
);

if ( ! empty( $request['search'] ) ) {
$query_args['search'] = $request['search'];
}

/**
* Filters the query arguments for a search request.
*
* Enables adding extra arguments or setting defaults for a term search request.
*
* @param array $query_args Key value array of query var to query value.
* @param WP_REST_Request $request The request used.
*/
$query_args = apply_filters( 'rest_term_search_query', $query_args, $request );

$query = new WP_Term_Query();
$found_terms = $query->query( $query_args );
$found_ids = wp_list_pluck( $found_terms, 'term_id' );

unset( $query_args['offset'], $query_args['number'] );

$total = wp_count_terms( $query_args );

// wp_count_terms() can return a falsey value when the term has no children.
if ( ! $total ) {
$total = 0;
}

return array(
self::RESULT_IDS => $found_ids,
self::RESULT_TOTAL => $total,
);
}

/**
* Prepares the search result for a given ID.
*
* @param int $id Item ID.
* @param array $fields Fields to include for the item.
* @return array Associative array containing all fields for the item.
*/
public function prepare_item( $id, array $fields ) {
$term = get_term( $id );

$data = array();

if ( in_array( WP_REST_Search_Controller::PROP_ID, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_ID ] = (int) $id;
}
if ( in_array( WP_REST_Search_Controller::PROP_TITLE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_TITLE ] = $term->name;
}
if ( in_array( WP_REST_Search_Controller::PROP_URL, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_URL ] = get_term_link( $id );
}
if ( in_array( WP_REST_Search_Controller::PROP_TYPE, $fields, true ) ) {
$data[ WP_REST_Search_Controller::PROP_TYPE ] = $term->taxonomy;
}

return $data;
}

/**
* Prepares links for the search result of a given ID.
*
* @param int $id Item ID.
* @return array Links for the given item.
*/
public function prepare_item_links( $id ) {
$term = get_term( $id );

$links = array();

$item_route = rest_get_route_for_term( $term );
if ( $item_route ) {
$links['self'] = array(
'href' => rest_url( $item_route ),
'embeddable' => true,
);
}

$links['about'] = array(
'href' => rest_url( sprintf( 'wp/v2/taxonomies/%s', $term->taxonomy ) ),
);

return $links;
}
}
6 changes: 6 additions & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ function gutenberg_is_experiment_enabled( $name ) {
if ( ! class_exists( 'WP_REST_Plugins_Controller' ) ) {
require_once dirname( __FILE__ ) . '/class-wp-rest-plugins-controller.php';
}
if ( ! class_exists( 'WP_REST_Post_Format_Search_Handler' ) ) {
require_once dirname( __FILE__ ) . '/class-wp-rest-post-format-search-handler.php';
}
if ( ! class_exists( 'WP_REST_Term_Search_Handler' ) ) {
require_once dirname( __FILE__ ) . '/class-wp-rest-term-search-handler.php';
}
/**
* End: Include for phase 2
*/
Expand Down
29 changes: 29 additions & 0 deletions lib/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,32 @@ function gutenberg_register_image_editor() {
}
}
add_filter( 'rest_api_init', 'gutenberg_register_image_editor' );

/**
* Registers the post format search handler.
*
* @param string $search_handlers Title list of current handlers.
*
* @return array Title updated list of handlers.
*/
function gutenberg_post_format_search_handler( $search_handlers ) {
if ( current_theme_supports( 'post-formats' ) ) {
$search_handlers[] = new WP_REST_Post_Format_Search_Handler();
}

return $search_handlers;
}
add_filter( 'wp_rest_search_handlers', 'gutenberg_post_format_search_handler', 10, 5 );

/**
* Registers the terms search handler.
*
* @param string $search_handlers Title list of current handlers.
*
* @return array Title updated list of handlers.
*/
function gutenberg_term_search_handler( $search_handlers ) {
$search_handlers[] = new WP_REST_Term_Search_Handler();
return $search_handlers;
}
add_filter( 'wp_rest_search_handlers', 'gutenberg_term_search_handler', 10, 5 );
59 changes: 46 additions & 13 deletions packages/edit-navigation/src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { map, set } from 'lodash';
import { map, set, flatten, partialRight } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -39,26 +39,56 @@ function disableInsertingNonNavigationBlocks( settings, name ) {
* It seems like there is no suitable package to import this from. Ideally it would be either part of core-data.
* Until we refactor it, just copying the code is the simplest solution.
*
* @param {Object} search
* @param {number} perPage
* @param {string} search
* @param {Object} [searchArguments]
* @param {number} [searchArguments.perPage=20]
* @param {Object} [editorSettings]
* @param {boolean} [editorSettings.disablePostFormats=false]
* @return {Promise<Object[]>} List of suggestions
*/
async function fetchLinkSuggestions( search, { perPage = 20 } = {} ) {
const posts = await apiFetch( {
const fetchLinkSuggestions = (
search,
{ perPage = 20 } = {},
{ disablePostFormats = false } = {}
) => {
const posts = apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: perPage,
type: 'post',
} ),
} );

return map( posts, ( post ) => ( {
id: post.id,
url: post.url,
title: decodeEntities( post.title ) || __( '(no title)' ),
type: post.subtype || post.type,
} ) );
}
const terms = apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: perPage,
type: 'term',
} ),
} );

let formats;
if ( disablePostFormats ) {
formats = Promise.resolve( [] );
} else {
formats = apiFetch( {
path: addQueryArgs( '/wp/v2/search', {
search,
per_page: perPage,
type: 'post-format',
} ),
} );
}

return Promise.all( [ posts, terms, formats ] ).then( ( results ) => {
return map( flatten( results ).slice( 0, perPage ), ( result ) => ( {
id: result.id,
url: result.url,
title: decodeEntities( result.title ) || __( '(no title)' ),
type: result.subtype || result.type,
} ) );
} );
};

export function initialize( id, settings ) {
if ( ! settings.blockNavMenus ) {
Expand All @@ -75,7 +105,10 @@ export function initialize( id, settings ) {
__experimentalRegisterExperimentalCoreBlocks( settings );
}

settings.__experimentalFetchLinkSuggestions = fetchLinkSuggestions;
settings.__experimentalFetchLinkSuggestions = partialRight(
fetchLinkSuggestions,
settings
);

render(
<Layout blockEditorSettings={ settings } />,
Expand Down
Loading