diff --git a/includes/class-newspack-blocks.php b/includes/class-newspack-blocks.php index 6db68c8c6..e16601c20 100644 --- a/includes/class-newspack-blocks.php +++ b/includes/class-newspack-blocks.php @@ -625,17 +625,18 @@ public static function build_articles_query( $attributes, $block_name ) { if ( current_user_can( 'edit_others_posts' ) && isset( $attributes['includedPostStatuses'] ) ) { $included_post_statuses = $attributes['includedPostStatuses']; } - $authors = isset( $attributes['authors'] ) ? $attributes['authors'] : array(); - $categories = isset( $attributes['categories'] ) ? $attributes['categories'] : array(); - $include_subcategories = isset( $attributes['includeSubcategories'] ) ? intval( $attributes['includeSubcategories'] ) : false; - $tags = isset( $attributes['tags'] ) ? $attributes['tags'] : array(); - $custom_taxonomies = isset( $attributes['customTaxonomies'] ) ? $attributes['customTaxonomies'] : array(); - $tag_exclusions = isset( $attributes['tagExclusions'] ) ? $attributes['tagExclusions'] : array(); - $category_exclusions = isset( $attributes['categoryExclusions'] ) ? $attributes['categoryExclusions'] : array(); - $specific_posts = isset( $attributes['specificPosts'] ) ? $attributes['specificPosts'] : array(); - $posts_to_show = intval( $attributes['postsToShow'] ); - $specific_mode = isset( $attributes['specificMode'] ) ? intval( $attributes['specificMode'] ) : false; - $args = array( + $authors = isset( $attributes['authors'] ) ? $attributes['authors'] : array(); + $categories = isset( $attributes['categories'] ) ? $attributes['categories'] : array(); + $include_subcategories = isset( $attributes['includeSubcategories'] ) ? intval( $attributes['includeSubcategories'] ) : false; + $tags = isset( $attributes['tags'] ) ? $attributes['tags'] : array(); + $custom_taxonomies = isset( $attributes['customTaxonomies'] ) ? $attributes['customTaxonomies'] : array(); + $tag_exclusions = isset( $attributes['tagExclusions'] ) ? $attributes['tagExclusions'] : array(); + $category_exclusions = isset( $attributes['categoryExclusions'] ) ? $attributes['categoryExclusions'] : array(); + $custom_taxonomy_exclusions = isset( $attributes['customTaxonomyExclusions'] ) ? $attributes['customTaxonomyExclusions'] : array(); + $specific_posts = isset( $attributes['specificPosts'] ) ? $attributes['specificPosts'] : array(); + $posts_to_show = intval( $attributes['postsToShow'] ); + $specific_mode = isset( $attributes['specificMode'] ) ? intval( $attributes['specificMode'] ) : false; + $args = array( 'post_type' => $post_type, 'post_status' => $included_post_statuses, 'suppress_filters' => false, @@ -694,6 +695,17 @@ public static function build_articles_query( $attributes, $block_name ) { } } } + if ( $custom_taxonomy_exclusions && count( $custom_taxonomy_exclusions ) ) { + foreach ( $custom_taxonomy_exclusions as $exclusion ) { + $args['tax_query'][] = [ + 'field' => 'term_id', + 'include_children' => false, + 'operator' => 'NOT IN', + 'taxonomy' => $exclusion['slug'], + 'terms' => $exclusion['terms'], + ]; + } + } $is_co_authors_plus_active = class_exists( 'CoAuthors_Guest_Authors' ); diff --git a/src/blocks/homepage-articles/block.json b/src/blocks/homepage-articles/block.json index 6a08d8937..5283bba97 100644 --- a/src/blocks/homepage-articles/block.json +++ b/src/blocks/homepage-articles/block.json @@ -137,6 +137,24 @@ "default": [], "items": { "type": "integer" } }, + "customTaxonomyExclusions": { + "type": "array", + "default": [], + "items": { + "type": "object", + "properties": { + "slug": { + "type": "string" + }, + "terms": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + }, "specificPosts": { "type": "array", "default": [], diff --git a/src/blocks/homepage-articles/class-wp-rest-newspack-articles-controller.php b/src/blocks/homepage-articles/class-wp-rest-newspack-articles-controller.php index 2e06891d9..4e38c9f30 100644 --- a/src/blocks/homepage-articles/class-wp-rest-newspack-articles-controller.php +++ b/src/blocks/homepage-articles/class-wp-rest-newspack-articles-controller.php @@ -187,7 +187,7 @@ public function get_items( $request ) { array_merge( array_map( function( $attribute ) { - return false === $attribute ? '0' : str_replace( '#', '%23', $attribute ); + return false === $attribute ? '0' : $attribute; }, $attributes ), diff --git a/src/blocks/homepage-articles/edit.js b/src/blocks/homepage-articles/edit.js index ab4445e6c..0fe27e691 100644 --- a/src/blocks/homepage-articles/edit.js +++ b/src/blocks/homepage-articles/edit.js @@ -293,6 +293,7 @@ class Edit extends Component { tags, tagExclusions, categoryExclusions, + customTaxonomyExclusions, } = attributes; const imageSizeOptions = [ @@ -370,6 +371,8 @@ class Edit extends Component { onTagExclusionsChange={ handleAttributeChange( 'tagExclusions' ) } categoryExclusions={ categoryExclusions } onCategoryExclusionsChange={ handleAttributeChange( 'categoryExclusions' ) } + customTaxonomyExclusions={ customTaxonomyExclusions } + onCustomTaxonomyExclusionsChange={ handleAttributeChange( 'customTaxonomyExclusions' ) } postType={ postType } /> { postLayout === 'grid' && ( diff --git a/src/blocks/homepage-articles/utils.ts b/src/blocks/homepage-articles/utils.ts index f631057ea..0bccfe67d 100644 --- a/src/blocks/homepage-articles/utils.ts +++ b/src/blocks/homepage-articles/utils.ts @@ -36,6 +36,7 @@ const POST_QUERY_ATTRIBUTES = [ 'specificMode', 'tagExclusions', 'categoryExclusions', + 'customTaxonomyExclusions', 'postType', 'includedPostStatuses', 'deduplicate', @@ -55,6 +56,7 @@ type HomepageArticlesAttributes = { specificMode: boolean; tagExclusions: TagId[]; categoryExclusions: CategoryId[]; + customTaxonomyExclusions: Taxonomy[]; }; type HomepageArticlesProps = { @@ -100,6 +102,7 @@ export const queryCriteriaFromAttributes = ( attributes: Block[ 'attributes' ] ) specificMode, tagExclusions, categoryExclusions, + customTaxonomyExclusions, includedPostStatuses, } = pick( attributes, POST_QUERY_ATTRIBUTES ); @@ -120,6 +123,7 @@ export const queryCriteriaFromAttributes = ( attributes: Block[ 'attributes' ] ) tags, tagExclusions, categoryExclusions, + customTaxonomyExclusions, customTaxonomies, postType, includedPostStatuses, diff --git a/src/blocks/homepage-articles/view.php b/src/blocks/homepage-articles/view.php index 203d6fd1a..14db6bdba 100644 --- a/src/blocks/homepage-articles/view.php +++ b/src/blocks/homepage-articles/view.php @@ -322,7 +322,7 @@ function newspack_blocks_render_block_homepage_articles( $attributes ) { array_merge( array_map( function( $attribute ) { - return false === $attribute ? '0' : str_replace( '#', '%23', $attribute ); + return false === $attribute ? '0' : $attribute; }, $attributes ), diff --git a/src/components/query-controls.js b/src/components/query-controls.js index d2a22e14e..ce142527d 100644 --- a/src/components/query-controls.js +++ b/src/components/query-controls.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; import { Button, QueryControls as BasicQueryControls, ToggleControl } from '@wordpress/components'; import apiFetch from '@wordpress/api-fetch'; @@ -233,20 +233,22 @@ class QueryControls extends Component { onTagExclusionsChange, categoryExclusions, onCategoryExclusionsChange, + customTaxonomyExclusions, + onCustomTaxonomyExclusionsChange, enableSpecific, } = this.props; const { showAdvancedFilters } = this.state; const registeredCustomTaxonomies = window.newspack_blocks_data?.custom_taxonomies; - const customTaxonomiesPrepareChange = ( taxSlug, value ) => { - let newValue = customTaxonomies.filter( tax => tax.slug !== taxSlug ); + const customTaxonomiesPrepareChange = ( taxArr, taxHandler, taxSlug, value ) => { + let newValue = taxArr.filter( tax => tax.slug !== taxSlug ); newValue = [ ...newValue, { slug: taxSlug, terms: value } ]; - onCustomTaxonomiesChange( newValue ); + taxHandler( newValue ); }; - const getTermsOfCustomTaxonomy = taxSlug => { - const tax = customTaxonomies.find( taxObj => taxObj.slug === taxSlug ); + const getTermsOfCustomTaxonomy = ( taxArr, taxSlug ) => { + const tax = taxArr.find( taxObj => taxObj.slug === taxSlug ); return tax ? tax.terms : []; }; @@ -312,9 +314,14 @@ class QueryControls extends Component { registeredCustomTaxonomies.map( tax => ( { - customTaxonomiesPrepareChange( tax.slug, value ); + customTaxonomiesPrepareChange( + customTaxonomies, + onCustomTaxonomiesChange, + tax.slug, + value + ); } } fetchSuggestions={ search => this.fetchCustomTaxonomiesSuggestions( tax.slug, search ) @@ -355,6 +362,31 @@ class QueryControls extends Component { label={ __( 'Excluded Categories', 'newspack-blocks' ) } /> ) } + { registeredCustomTaxonomies && + onCustomTaxonomyExclusionsChange && + registeredCustomTaxonomies.map( ( { label, slug } ) => ( + this.fetchSavedCustomTaxonomies( slug, termIds ) } + fetchSuggestions={ search => + this.fetchCustomTaxonomiesSuggestions( slug, search ) + } + key={ `${ slug }-exclusions-selector` } + label={ sprintf( + // translators: %s is the custom taxonomy label. + __( 'Excluded %s', 'newspack-blocks' ), + label + ) } + onChange={ value => + customTaxonomiesPrepareChange( + customTaxonomyExclusions, + onCustomTaxonomyExclusionsChange, + slug, + value + ) + } + tokens={ getTermsOfCustomTaxonomy( customTaxonomyExclusions, slug ) } + /> + ) ) } ) } @@ -372,6 +404,7 @@ QueryControls.defaultProps = { tags: [], customTaxonomies: [], tagExclusions: [], + customTaxonomyExclusions: [], }; export default QueryControls;