From 06868f4cbdfa265c70a6597da5d7675b4a18be6e Mon Sep 17 00:00:00 2001 From: Greg Marshall Date: Tue, 24 Oct 2023 09:25:29 -0500 Subject: [PATCH 1/9] WIP: issue-73 From 3b566765d4264418afc8fc8fc141de1951bda576 Mon Sep 17 00:00:00 2001 From: Greg Marshall Date: Tue, 24 Oct 2023 16:56:15 -0500 Subject: [PATCH 2/9] editor --- blocks/query/block.json | 20 +++++++++++ blocks/query/edit.tsx | 64 ++++++++++++++++++++++++++++++++++- blocks/query/types.ts | 4 +++ services/deduplicate/index.ts | 4 +-- 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/blocks/query/block.json b/blocks/query/block.json index e63c62fc..5a0f498b 100644 --- a/blocks/query/block.json +++ b/blocks/query/block.json @@ -74,6 +74,26 @@ "type": "array" }, "type": "object" + }, + "termRelations": { + "default": {}, + "items": { + "default": "AND", + "enum": [ + "AND", + "OR" + ], + "type": "string" + }, + "type": "object" + }, + "taxRelation": { + "default": "AND", + "enum": [ + "AND", + "OR" + ], + "type": "string" } } } diff --git a/blocks/query/edit.tsx b/blocks/query/edit.tsx index 2b887286..40b90cf2 100644 --- a/blocks/query/edit.tsx +++ b/blocks/query/edit.tsx @@ -9,6 +9,7 @@ import { PanelRow, RadioControl, RangeControl, + SelectControl, TextControl, } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; @@ -59,6 +60,8 @@ export default function Edit({ postTypes = ['post'], searchTerm = '', terms = {}, + termRelations = {}, + taxRelation = 'AND', }, setAttributes, }: EditProps) { @@ -93,16 +96,27 @@ export default function Edit({ const [availableTaxonomies, setAvailableTaxonomies] = useState({}); const [availableTypes, setAvailableTypes] = useState({}); + const taxCount = allowedTaxonomies.reduce((acc: number, taxonomy: string) => { + const hasTax = terms[taxonomy]?.length > 0 ? 1 : 0; + return acc + hasTax; + }, 0); + let termQueryArgs = ''; if (Object.keys(availableTaxonomies).length > 0) { allowedTaxonomies.forEach((taxonomy) => { if (terms[taxonomy]?.length > 0) { const restBase = availableTaxonomies[taxonomy].rest_base; if (restBase) { - termQueryArgs += `&${restBase}=${terms[taxonomy].map((term) => term.id).join(',')}`; + termQueryArgs += `&${restBase}[terms]=${terms[taxonomy].map((term) => term.id).join(',')}`; + if (termRelations[taxonomy] !== '' && typeof termRelations[taxonomy] !== 'undefined') { + termQueryArgs += `&${restBase}[operator]=${termRelations[taxonomy]}`; + } } } }); + if (taxCount > 1) { + termQueryArgs += `&tax_relation=${taxRelation}`; + } } const manualPostIds = manualPosts.map((post) => (post ?? null)).join(','); @@ -200,6 +214,14 @@ export default function Edit({ setAttributes({ terms: newTermAttrs }); }); + const setTermRelation = ((type: string, relation: string) => { + const newTermRelationAttrs = { + ...termRelations, + [type]: relation, + }; + setAttributes({ termRelations: newTermRelationAttrs }); + }); + const setNumberOfPosts = (newValue?: number) => { setAttributes({ numberOfPosts: newValue, @@ -319,9 +341,49 @@ export default function Edit({ onSelect={(newCategories: Term[]) => setTerms(taxonomy, newCategories)} multiple /> + {terms[taxonomy]?.length > 1 ? ( + setTermRelation(taxonomy, newValue)} + value={termRelations[taxonomy]} + /> + ) : null} +
)) ) : null} + {taxCount > 1 ? ( + setAttributes({ taxRelation: newValue })} + value={taxRelation} + /> + ) : null } setAttributes({ searchTerm: next })} diff --git a/blocks/query/types.ts b/blocks/query/types.ts index df1ff4cc..d1d1a49c 100644 --- a/blocks/query/types.ts +++ b/blocks/query/types.ts @@ -15,6 +15,10 @@ interface EditProps { terms?: { [key: string]: any[]; }; + termRelations?: { + [key: string]: string; + }; + taxRelation?: string; }; setAttributes: (attributes: any) => void; } diff --git a/services/deduplicate/index.ts b/services/deduplicate/index.ts index 71442672..fbb6a09d 100644 --- a/services/deduplicate/index.ts +++ b/services/deduplicate/index.ts @@ -121,13 +121,13 @@ export function mainDedupe() { queryBlocks.forEach((queryBlock) => { const { attributes } = queryBlock; const { - backfillPosts = [], + backfillPosts = null, deduplication = 'inherit', posts = [], numberOfPosts = 5, postTypes = ['post'], } = attributes; - if (!backfillPosts.length) { + if (!backfillPosts) { return; } const postTypeString = postTypes.join(','); From f29010dc590647e217f9ce0aec3302c80aee36c1 Mon Sep 17 00:00:00 2001 From: Greg Marshall Date: Tue, 24 Oct 2023 17:09:59 -0500 Subject: [PATCH 3/9] front end --- src/class-plugin-curated-posts.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/class-plugin-curated-posts.php b/src/class-plugin-curated-posts.php index 1a43bd95..acd0d549 100644 --- a/src/class-plugin-curated-posts.php +++ b/src/class-plugin-curated-posts.php @@ -51,7 +51,7 @@ public function with_query_context( array $context, array $attributes, WP_Block_ if ( isset( $attributes['terms'] ) && is_array( $attributes['terms'] ) && count( $attributes['terms'] ) > 0 ) { $args['tax_query'] = [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query - 'relation' => 'AND', + 'relation' => $attributes['taxRelation'] ?? 'AND', ]; foreach ( $attributes['terms'] as $taxonomy => $terms ) { @@ -59,6 +59,7 @@ public function with_query_context( array $context, array $attributes, WP_Block_ $args['tax_query'][] = [ 'taxonomy' => $taxonomy, 'terms' => array_column( $terms, 'id' ), + 'operator' => $attributes['termsRelation'][ $taxonomy ] ?? 'AND', ]; } } From 606f5741b0ed1d0c09d22cee564eac57c71c099e Mon Sep 17 00:00:00 2001 From: Greg Marshall Date: Tue, 24 Oct 2023 17:11:08 -0500 Subject: [PATCH 4/9] lint --- blocks/query/edit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/query/edit.tsx b/blocks/query/edit.tsx index 40b90cf2..4d2960c6 100644 --- a/blocks/query/edit.tsx +++ b/blocks/query/edit.tsx @@ -345,7 +345,7 @@ export default function Edit({ Date: Wed, 25 Oct 2023 23:21:31 -0500 Subject: [PATCH 5/9] phpstan --- src/class-plugin-curated-posts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class-plugin-curated-posts.php b/src/class-plugin-curated-posts.php index acd0d549..3b6a9d78 100644 --- a/src/class-plugin-curated-posts.php +++ b/src/class-plugin-curated-posts.php @@ -59,7 +59,7 @@ public function with_query_context( array $context, array $attributes, WP_Block_ $args['tax_query'][] = [ 'taxonomy' => $taxonomy, 'terms' => array_column( $terms, 'id' ), - 'operator' => $attributes['termsRelation'][ $taxonomy ] ?? 'AND', + 'operator' => is_array( $attributes['termRelations'] ) ? $attributes['termRelations'][ $taxonomy ] ?? 'AND' : 'AND', ]; } } From 73c289422cbf759747fa249143316fc75e27e914 Mon Sep 17 00:00:00 2001 From: Greg Marshall Date: Wed, 25 Oct 2023 23:35:42 -0500 Subject: [PATCH 6/9] bump to 1.2.0 --- wp-curate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wp-curate.php b/wp-curate.php index a4abd0e5..986e4ee6 100644 --- a/wp-curate.php +++ b/wp-curate.php @@ -3,7 +3,7 @@ * Plugin Name: WP Curate * Plugin URI: https://github.com/alleyinteractive/wp-curate * Description: Plugin to curate homepages and other landing pages - * Version: 1.1.0 + * Version: 1.2.0 * Author: Alley Interactive * Author URI: https://github.com/alleyinteractive/wp-curate * Requires at least: 6.3 From 4266e7bbff85dfd20c4a1a7d90e35b1f84d7d8ad Mon Sep 17 00:00:00 2001 From: Greg Marshall Date: Thu, 26 Oct 2023 11:23:40 -0500 Subject: [PATCH 7/9] code review feedback --- CHANGELOG.md | 10 ++++- blocks/query/edit.tsx | 63 +++++++++++----------------- services/buildTermQueryArgs/index.ts | 40 ++++++++++++++++++ 3 files changed, 73 insertions(+), 40 deletions(-) create mode 100644 services/buildTermQueryArgs/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ff61d584..25a7f439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to `WP Curate` will be documented in this file. -## 0.1.0 - 202X-XX-XX +## 1.2.0 - 2023-10-26 + +- Adds support for AND/OR operators in the Query Parameters, giving more control over what posts to show. + +## 1.1.0 - 2023-09-21 + +- Bug fix: prevents error if post type does not support meta. + +## 1.0.0 - 2023-09-19 - Initial release diff --git a/blocks/query/edit.tsx b/blocks/query/edit.tsx index 4d2960c6..71ce9d3b 100644 --- a/blocks/query/edit.tsx +++ b/blocks/query/edit.tsx @@ -31,6 +31,8 @@ import { mainDedupe, } from '../../services/deduplicate'; +import buildTermQueryArgs from '../../services/buildTermQueryArgs'; + import './index.scss'; interface Window { @@ -72,6 +74,17 @@ export default function Edit({ } = {}, } = (window as any as Window); + const andOrOptions = [ + { + label: __('AND', 'wp-curate'), + value: 'AND', + }, + { + label: __('OR', 'wp-curate'), + value: 'OR', + }, + ]; + // @ts-ignore const [isPostDeduplicating, postTypeObject] = useSelect( (select) => { @@ -101,23 +114,13 @@ export default function Edit({ return acc + hasTax; }, 0); - let termQueryArgs = ''; - if (Object.keys(availableTaxonomies).length > 0) { - allowedTaxonomies.forEach((taxonomy) => { - if (terms[taxonomy]?.length > 0) { - const restBase = availableTaxonomies[taxonomy].rest_base; - if (restBase) { - termQueryArgs += `&${restBase}[terms]=${terms[taxonomy].map((term) => term.id).join(',')}`; - if (termRelations[taxonomy] !== '' && typeof termRelations[taxonomy] !== 'undefined') { - termQueryArgs += `&${restBase}[operator]=${termRelations[taxonomy]}`; - } - } - } - }); - if (taxCount > 1) { - termQueryArgs += `&tax_relation=${taxRelation}`; - } - } + const termQueryArgs = buildTermQueryArgs( + allowedTaxonomies, + terms, + availableTaxonomies, + termRelations, + taxRelation, + ); const manualPostIds = manualPosts.map((post) => (post ?? null)).join(','); const postTypeString = postTypes.join(','); @@ -157,7 +160,7 @@ export default function Edit({ per_page: 20, }, ); - path += termQueryArgs; + path += `&${termQueryArgs}`; apiFetch({ path, @@ -348,18 +351,9 @@ export default function Edit({ availableTaxonomies[taxonomy].name || taxonomy, )} help={__('AND: Posts must have all selected terms. OR: Posts may have one or more selected terms.', 'wp-curate')} - options={[ - { - label: __('AND', 'wp-curate'), - value: 'AND', - }, - { - label: __('OR', 'wp-curate'), - value: 'OR', - }, - ]} + options={andOrOptions} onChange={(newValue) => setTermRelation(taxonomy, newValue)} - value={termRelations[taxonomy]} + value={termRelations[taxonomy] ?? 'OR'} /> ) : null}
@@ -370,16 +364,7 @@ export default function Edit({ setAttributes({ taxRelation: newValue })} value={taxRelation} /> diff --git a/services/buildTermQueryArgs/index.ts b/services/buildTermQueryArgs/index.ts new file mode 100644 index 00000000..2320a05d --- /dev/null +++ b/services/buildTermQueryArgs/index.ts @@ -0,0 +1,40 @@ +/** + * Builds the term query args for the WP REST API. + * + * @param string[] allowedTaxonomies The list of allowed taxonomies. + * @param { [key: string]: any[] } terms The selected terms. + * @param { [key: string]: any[] } availableTaxonomies The available taxonomies. + * @param { [key: string]: string } termRelations The AND/OR relation used for each taxonomy. + * @param string taxRelation The AND/OR relation used for all the terms. + * @returns string The term query args. + */ +export default function buildTermQueryArts( + allowedTaxonomies: string[], + terms: { [key: string]: any[] }, + availableTaxonomies: { [key: string]: any }, + termRelations: { [key: string]: string }, + taxRelation: string, +): string { + const taxCount = allowedTaxonomies.reduce((acc: number, taxonomy: string) => { + const hasTax = terms[taxonomy]?.length > 0 ? 1 : 0; + return acc + hasTax; + }, 0); + const termQueryArgs: string[] = []; + if (Object.keys(availableTaxonomies).length > 0) { + allowedTaxonomies.forEach((taxonomy) => { + if (terms[taxonomy]?.length > 0) { + const restBase = availableTaxonomies[taxonomy].rest_base; + if (restBase) { + termQueryArgs.push(`${restBase}[terms]=${terms[taxonomy].map((term) => term.id).join(',')}`); + if (termRelations[taxonomy] !== '' && typeof termRelations[taxonomy] !== 'undefined') { + termQueryArgs.push(`${restBase}[operator]=${termRelations[taxonomy]}`); + } + } + } + }); + if (taxCount > 1) { + termQueryArgs.push(`tax_relation=${taxRelation}`); + } + } + return termQueryArgs.join('&'); +} From aea131a07b5f3a2650c8ca8cd7497e3b4ae3488d Mon Sep 17 00:00:00 2001 From: Greg Marshall Date: Thu, 26 Oct 2023 11:35:20 -0500 Subject: [PATCH 8/9] coderabbit suggestions --- blocks/query/edit.tsx | 5 +---- services/buildTermQueryArgs/index.ts | 8 +++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/blocks/query/edit.tsx b/blocks/query/edit.tsx index 71ce9d3b..76cc7974 100644 --- a/blocks/query/edit.tsx +++ b/blocks/query/edit.tsx @@ -109,10 +109,7 @@ export default function Edit({ const [availableTaxonomies, setAvailableTaxonomies] = useState({}); const [availableTypes, setAvailableTypes] = useState({}); - const taxCount = allowedTaxonomies.reduce((acc: number, taxonomy: string) => { - const hasTax = terms[taxonomy]?.length > 0 ? 1 : 0; - return acc + hasTax; - }, 0); + const taxCount = allowedTaxonomies.filter((taxonomy: string) => terms[taxonomy]?.length > 0).length; // eslint-disable-line max-len const termQueryArgs = buildTermQueryArgs( allowedTaxonomies, diff --git a/services/buildTermQueryArgs/index.ts b/services/buildTermQueryArgs/index.ts index 2320a05d..52e17f41 100644 --- a/services/buildTermQueryArgs/index.ts +++ b/services/buildTermQueryArgs/index.ts @@ -8,17 +8,15 @@ * @param string taxRelation The AND/OR relation used for all the terms. * @returns string The term query args. */ -export default function buildTermQueryArts( +export default function buildTermQueryArgs( allowedTaxonomies: string[], terms: { [key: string]: any[] }, availableTaxonomies: { [key: string]: any }, termRelations: { [key: string]: string }, taxRelation: string, ): string { - const taxCount = allowedTaxonomies.reduce((acc: number, taxonomy: string) => { - const hasTax = terms[taxonomy]?.length > 0 ? 1 : 0; - return acc + hasTax; - }, 0); + const taxCount = allowedTaxonomies.filter((taxonomy: string) => terms[taxonomy]?.length > 0).length; // eslint-disable-line max-len + const termQueryArgs: string[] = []; if (Object.keys(availableTaxonomies).length > 0) { allowedTaxonomies.forEach((taxonomy) => { From 3fe4fe9db16697752ee607b58c34019fe103b7a4 Mon Sep 17 00:00:00 2001 From: Greg Marshall Date: Thu, 26 Oct 2023 11:44:57 -0500 Subject: [PATCH 9/9] add type for taxonomies --- services/buildTermQueryArgs/index.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/services/buildTermQueryArgs/index.ts b/services/buildTermQueryArgs/index.ts index 52e17f41..f38151d4 100644 --- a/services/buildTermQueryArgs/index.ts +++ b/services/buildTermQueryArgs/index.ts @@ -1,3 +1,11 @@ +interface Types { + [key: string]: { + name: string; + slug: string; + rest_base: string; + }; +} + /** * Builds the term query args for the WP REST API. * @@ -11,7 +19,7 @@ export default function buildTermQueryArgs( allowedTaxonomies: string[], terms: { [key: string]: any[] }, - availableTaxonomies: { [key: string]: any }, + availableTaxonomies: Types, termRelations: { [key: string]: string }, taxRelation: string, ): string {