diff --git a/includes/query-loop.php b/includes/query-loop.php index d98e35b..c3563a6 100644 --- a/includes/query-loop.php +++ b/includes/query-loop.php @@ -45,6 +45,11 @@ function parse_meta_query( $meta_query_data ) { function get_exclude_ids( $attributes ) { $exclude_ids = array(); + // Exclude Posts by ID. + if ( isset( $attributes['exclude_posts'] ) && ! empty( $attributes['exclude_posts'] ) ) { + $exclude_ids = $attributes['exclude_posts']; + } + // Exclude Current Post. if ( isset( $attributes['exclude_current'] ) ) { if ( is_int( $attributes['exclude_current'] ) || ! preg_match( '/[a-z\-]+\/\/[a-z\-]+/', $attributes['exclude_current'] ) ) { @@ -275,9 +280,11 @@ function add_custom_query_params( $args, $request ) { } // Exclusion Related. + $exclude_posts = $request->get_param( 'exclude_posts' ); $exclude_current = $request->get_param( 'exclude_current' ); - if ( $exclude_current ) { + if ( $exclude_posts || $exclude_current ) { $attributes = array( + 'exclude_posts' => $exclude_posts, 'exclude_current' => $exclude_current, ); diff --git a/src/components/post-exclude-controls.js b/src/components/post-exclude-controls.js index 7fc3949..30d8ad8 100644 --- a/src/components/post-exclude-controls.js +++ b/src/components/post-exclude-controls.js @@ -1,7 +1,11 @@ /** * WordPress dependencies */ -import { ToggleControl } from '@wordpress/components'; +import { + ToggleControl, + FormTokenField, + BaseControl, +} from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { useEntityRecord } from '@wordpress/core-data'; import { __ } from '@wordpress/i18n'; @@ -16,13 +20,51 @@ import { __ } from '@wordpress/i18n'; * @return {Element} PostExcludeControls */ export const PostExcludeControls = ( { attributes, setAttributes } ) => { - const { query: { exclude_current: excludeCurrent } = {} } = attributes; + const { + query: { + exclude_posts: excludePosts = [], + exclude_current: excludeCurrent, + postType, + multiple_posts: multiplePosts = [], + } = {}, + } = attributes; const { record: siteOptions } = useEntityRecord( 'root', 'site' ); const currentPost = useSelect( ( select ) => { return select( 'core/editor' ).getCurrentPost(); }, [] ); - if ( ! currentPost ) { + // Get the posts for all post types used in the query. + const posts = useSelect( + ( select ) => { + const { getEntityRecords } = select( 'core' ); + + // Fetch posts for each post type and combine them into one array + return [ ...multiplePosts, postType ].reduce( + ( accumulator, postType ) => { + // Depending on the number of posts this could take a while, since we can't paginate here + const records = getEntityRecords( 'postType', postType, { + per_page: -1, + } ); + return [ ...accumulator, ...( records || [] ) ]; + }, + [] + ); + }, + [ postType, multiplePosts ] + ); + + // For use with flatMap(), as this lets us remove elements during a map() + const idToTitle = ( id ) => { + const post = posts.find( ( p ) => p.id === id ); + return post ? [ post.title.rendered ] : []; + }; + + const titleToId = ( title ) => { + const post = posts.find( ( p ) => p.title.rendered === title ); + return post ? [ post.id ] : []; + }; + + if ( ! currentPost || ! posts ) { return
{ __( 'Loading…', 'advanced-query-loop' ) }
; } @@ -47,6 +89,32 @@ export const PostExcludeControls = ( { attributes, setAttributes } ) => { return ( <>

{ __( 'Exclude Posts', 'advanced-query-loop' ) }

+ + idToTitle( id ) ) } + suggestions={ posts.map( ( post ) => post.title.rendered ) } + onChange={ ( titles ) => { + // Converts the Titles to Post IDs before saving them + setAttributes( { + query: { + ...attributes.query, + exclude_posts: + titles.flatMap( ( title ) => + titleToId( title ) + ) || [], + }, + } ); + } } + __experimentalExpandOnFocus + __experimentalShowHowTo={ false } + /> +