Skip to content

Commit b82c974

Browse files
committed
feat: content rule exclusion logic and tests
1 parent 98dcbc9 commit b82c974

File tree

6 files changed

+72
-25
lines changed

6 files changed

+72
-25
lines changed

includes/content-gate/class-content-restriction-control.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,10 @@ public static function get_post_gates( $post_id = null ) {
121121
}
122122

123123
foreach ( $content_rules as $content_rule ) {
124+
$is_exclusion = isset( $content_rule['exclusion'] ) && $content_rule['exclusion'];
124125
if ( $content_rule['slug'] === 'post_types' ) {
125126
$post_type = get_post_type( $post_id );
126-
if ( ! in_array( $post_type, $content_rule['value'], true ) ) {
127+
if ( $is_exclusion ? in_array( $post_type, $content_rule['value'], true ) : ! in_array( $post_type, $content_rule['value'], true ) ) {
127128
continue 2;
128129
}
129130
} else {
@@ -132,10 +133,10 @@ public static function get_post_gates( $post_id = null ) {
132133
continue 2;
133134
}
134135
$terms = wp_get_post_terms( $post_id, $content_rule['slug'], [ 'fields' => 'ids' ] );
135-
if ( ! $terms || is_wp_error( $terms ) ) {
136+
if ( ( ! $is_exclusion && ! $terms ) || is_wp_error( $terms ) ) {
136137
continue 2;
137138
}
138-
if ( empty( array_intersect( $terms, $content_rule['value'] ) ) ) {
139+
if ( $is_exclusion ? ! empty( array_intersect( $terms, $content_rule['value'] ) ) : empty( array_intersect( $terms, $content_rule['value'] ) ) ) {
139140
continue 2;
140141
}
141142
}

includes/wizards/audience/class-audience-content-gates.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,9 @@ public function register_api_endpoints() {
242242
'items' => [
243243
'type' => 'object',
244244
'properties' => [
245-
'slug' => [ 'type' => 'string' ],
246-
'value' => [ 'type' => 'mixed' ],
245+
'slug' => [ 'type' => 'string' ],
246+
'value' => [ 'type' => 'mixed' ],
247+
'exclusion' => [ 'type' => 'boolean' ],
247248
],
248249
],
249250
],
@@ -388,12 +389,18 @@ public function sanitize_content_rule( $content_rule ) {
388389
}
389390
}
390391

391-
$value = array_values( array_filter( array_map( 'sanitize_text_field', $content_rule['value'] ) ) );
392+
$value = array_values( array_filter( array_map( 'sanitize_text_field', $content_rule['value'] ) ) );
393+
$exclusion = isset( $content_rule['exclusion'] ) ? boolval( $content_rule['exclusion'] ) : false;
392394

393-
return [
395+
$sanitized_rule = [
394396
'slug' => $slug,
395397
'value' => $value,
396398
];
399+
if ( $exclusion ) {
400+
$sanitized_rule['exclusion'] = $exclusion;
401+
}
402+
403+
return $sanitized_rule;
397404
}
398405

399406
/**

src/wizards/audience/views/content-gates/content-rule-control.tsx

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,38 @@ import { CheckboxControl } from '@wordpress/components';
1414
import ContentRuleControlTaxonomy from './content-rule-control-taxonomy';
1515
import { FormTokenField } from '../../../../../packages/components/src';
1616

17-
const noop = () => {};
18-
19-
export default function ContentRuleControl( { slug, value, onChange }: GateContentRuleControlProps ) {
17+
export default function ContentRuleControl( { slug, value, exclusion, onChange, onChangeExclusion }: GateContentRuleControlProps ) {
2018
const rule = window.newspackAudienceContentGates.available_content_rules[ slug ];
2119

2220
if ( ! rule || ! Array.isArray( value ) ) {
2321
return null;
2422
}
2523

26-
if ( rule.options && rule.options.length > 0 ) {
27-
return (
28-
<div className="newspack-content-gates__content-rule-control">
24+
return (
25+
<div className="newspack-content-gates__content-rule-control">
26+
{ rule.options && rule.options.length > 0 ? (
2927
<FormTokenField
3028
label={ rule.name }
3129
value={ rule.options.filter( o => value.includes( o.value ) ).map( o => o.label ) }
3230
onChange={ ( items: string[] ) => onChange( rule.options?.filter( o => items.includes( o.label ) ).map( o => o.value ) ?? [] ) }
3331
suggestions={ rule.options.map( o => o.label ) }
3432
__experimentalExpandOnFocus={ true }
3533
/>
36-
<CheckboxControl
37-
label={ __( 'Exclusion rule', 'newspack-plugin' ) }
38-
help={ __( 'Apply this rule to everything EXCEPT the items matching the above.', 'newspack-plugin' ) }
39-
checked={ false }
40-
onChange={ noop }
34+
) : (
35+
<ContentRuleControlTaxonomy
36+
slug={ slug }
37+
value={ value }
38+
exclusion={ exclusion }
39+
onChange={ onChange }
40+
onChangeExclusion={ onChangeExclusion }
4141
/>
42-
</div>
43-
);
44-
}
45-
46-
return <ContentRuleControlTaxonomy slug={ slug } value={ value } onChange={ onChange } />;
42+
) }
43+
<CheckboxControl
44+
label={ __( 'Exclusion rule', 'newspack-plugin' ) }
45+
help={ __( 'Apply this rule to everything EXCEPT the items matching the above.', 'newspack-plugin' ) }
46+
checked={ exclusion ?? false }
47+
onChange={ e => onChangeExclusion( e ) }
48+
/>
49+
</div>
50+
);
4751
}

src/wizards/audience/views/content-gates/content-rules.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ export default function ContentRules( { rules, onChange }: ContentRulesProps ) {
5151
[ onChange, rules ]
5252
);
5353

54+
const handleChangeExclusion = useCallback(
55+
( slug: string ) => ( e: boolean ) => {
56+
onChange( rules.map( r => ( r.slug === slug ? { ...r, exclusion: e } : r ) ) );
57+
},
58+
[ onChange, rules ]
59+
);
60+
5461
return (
5562
<ActionCard
5663
title={ __( 'Content Rules', 'newspack-plugin' ) }
@@ -66,7 +73,14 @@ export default function ContentRules( { rules, onChange }: ContentRulesProps ) {
6673
>
6774
<Grid columns={ Math.min( 3, rules.length ) } gutter={ 32 }>
6875
{ rules.map( ( rule: GateContentRule ) => (
69-
<ContentRuleControl key={ rule.slug } slug={ rule.slug } value={ rule.value } onChange={ handleChange( rule.slug ) } />
76+
<ContentRuleControl
77+
key={ rule.slug }
78+
slug={ rule.slug }
79+
value={ rule.value }
80+
exclusion={ rule.exclusion }
81+
onChange={ handleChange( rule.slug ) }
82+
onChangeExclusion={ handleChangeExclusion( rule.slug ) }
83+
/>
7084
) ) }
7185
</Grid>
7286
</ActionCard>

src/wizards/audience/views/content-gates/types/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,15 @@ type GateAccessRuleControlProps = {
5050
type GateContentRule = {
5151
slug: string;
5252
value: string[];
53+
exclusion?: boolean;
5354
};
5455

5556
type GateContentRuleControlProps = {
5657
slug: string;
5758
value: GateContentRuleValue;
59+
exclusion?: boolean;
5860
onChange: (value: GateContentRuleValue) => void;
61+
onChangeExclusion: (value: boolean) => void;
5962
};
6063

6164
type GateStatus = 'publish' | 'draft' | 'pending' | 'future' | 'private' | 'trash';
@@ -70,5 +73,4 @@ type Gate = {
7073
priority: number;
7174
status: GateStatus;
7275
isExpanded?: boolean;
73-
collapse?: boolean;
7476
};

tests/unit-tests/content-gate/content-gates.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,5 +220,24 @@ public function test_content_rules() {
220220

221221
$gates = Content_Restriction_Control::get_post_gates( $post2 );
222222
$this->assertCount( 0, $gates, 'No gates for the post in category 2' );
223+
224+
// Make the content rule an exclusion rule.
225+
Content_Gate::update_post_content_rules(
226+
$this->gate_ids[2],
227+
[
228+
[
229+
'slug' => 'category',
230+
'value' => [ $cat1 ],
231+
'exclusion' => true,
232+
],
233+
]
234+
);
235+
236+
$gates = Content_Restriction_Control::get_post_gates( $post1 );
237+
$this->assertCount( 0, $gates, 'No gates for the post in category 1' );
238+
239+
$gates = Content_Restriction_Control::get_post_gates( $post2 );
240+
$this->assertCount( 1, $gates, 'One gate for the post in category 2' );
241+
$this->assertEquals( $this->gate_ids[2], $gates[0]['id'], 'Gate with publish status and matching rules configuration is included' );
223242
}
224243
}

0 commit comments

Comments
 (0)