PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / 2.0.12
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More v2.0.12
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 47 releases
content-control / classes / RuleEngine / Rules.php

Rules.php in Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More 2.0.12, at classes/RuleEngine/Rules.php

668 lines 19.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Rule registery
4 *
5 * @package ContentControl
6 */
7
8 namespace ContentControl\RuleEngine;
9
10 defined( 'ABSPATH' ) || exit;
11
12 use ContentControl\Models\RuleEngine\Rule;
13
14 /**
15 * Rules registry
16 */
17 class Rules {
18
19 /**
20 * Array of rules.
21 *
22 * @var array<string,array<string,mixed>>
23 */
24 private $data = [];
25
26 /**
27 * Current global rule instance.
28 *
29 * @var Rule
30 */
31 public $current_rule = null;
32
33 /**
34 * Get the current global rule instance.
35 *
36 * @param Rule|null|false $rule Rule instance to set.
37 * @return Rule|null
38 */
39 public function current_rule( $rule = false ) {
40 if ( false === $rule ) {
41 return $this->current_rule;
42 }
43
44 $this->current_rule = $rule;
45 return $rule;
46 }
47
48 /**
49 * Set up rules list.
50 *
51 * @return void
52 */
53 public function init() {
54 // Register rules.
55 $this->register_built_in_rules();
56
57 // Register deprecated rules (using old filter).
58 $this->register_deprecated_rules();
59 }
60
61 /**
62 * Register new rule type.
63 *
64 * @param array<string,mixed> $rule New rule to register.
65 * @return void
66 */
67 public function register_rule( $rule ) {
68 if ( $this->is_rule_valid( $rule ) ) {
69 $rule = wp_parse_args( $rule, $this->get_rule_defaults() );
70
71 $index = $rule['name'];
72 /**
73 * In the case multiple conditions are registered with the same
74 * identifier, we append an integer. This do/while quickly increases
75 * the integer by one until a valid new key is found.
76 */
77 if ( array_key_exists( $index, $this->data ) ) {
78 $i = 0;
79 do {
80 ++$i;
81 $index = $rule['name'] . '-' . $i;
82 } while ( array_key_exists( $index, $this->data ) );
83 }
84
85 $indexs[] = $index;
86 $this->data[ $index ] = $rule;
87 }
88 }
89
90 /**
91 * Check if rule is valid.
92 *
93 * @param array<string,mixed> $rule Rule to test.
94 * @return boolean
95 */
96 public function is_rule_valid( $rule ) {
97 return is_array( $rule ) && ! empty( $rule );
98 }
99
100 /**
101 * Get array of all registered rules.
102 *
103 * @return array<string,array<string,mixed>>
104 */
105 public function get_rules() {
106 if ( ! did_action( 'content_control/rule_engine/register_rules' ) ) {
107 $this->init();
108 }
109
110 return $this->data;
111 }
112
113 /**
114 * Get a rule definition by name.
115 *
116 * @param string $rule_name Rule definition or null.
117 * @return array<string,mixed>|null
118 */
119 public function get_rule( $rule_name ) {
120 $rules = $this->get_rules();
121 return isset( $rules[ $rule_name ] ) ? $rules[ $rule_name ] : null;
122 }
123
124 /**
125 * Get array of registered rules filtered for the block-editor.
126 *
127 * @return array<string,array<string,mixed>>
128 */
129 public function get_block_editor_rules() {
130 $rules = $this->get_rules();
131
132 /**
133 * Filter the rules.
134 *
135 * @param array $rules Rules.
136 *
137 * @return array
138 */
139 return apply_filters( 'content_control/rule_engine/get_block_editor_rules', $rules );
140 }
141
142 /**
143 * Get list of verbs.
144 *
145 * @return array<string,string> List of verbs with translatable text.
146 */
147 public function get_verbs() {
148 return [
149 'are' => __( 'Are', 'content-control' ),
150 'arenot' => __( 'Are Not', 'content-control' ),
151 'is' => __( 'Is', 'content-control' ),
152 'isnot' => __( 'Is Not', 'content-control' ),
153 'has' => __( 'Has', 'content-control' ),
154 'hasnot' => __( 'Has Not', 'content-control' ),
155 'can' => __( 'Can', 'content-control' ),
156 'cannot' => __( 'Can Not', 'content-control' ),
157 'doesnothave' => __( 'Does Not Have', 'content-control' ),
158 'does' => __( 'Does', 'content-control' ),
159 'doesnot' => __( 'Does Not', 'content-control' ),
160 'was' => __( 'Was', 'content-control' ),
161 'wasnot' => __( 'Was Not', 'content-control' ),
162 'were' => __( 'Were', 'content-control' ),
163 'werenot' => __( 'Were Not', 'content-control' ),
164 ];
165 }
166
167 /**
168 * Get a list of built in rules.
169 *
170 * @return void
171 */
172 protected function register_built_in_rules() {
173 $rules = array_merge(
174 $this->get_user_rules(),
175 $this->get_general_content_rules(),
176 $this->get_post_type_rules(),
177 $this->get_taxonomy_rules()
178 );
179
180 /**
181 * Allow registering additional rules quickly.
182 *
183 * @param array<string,array<string,mixed>> $rules Rules.
184 *
185 * @return array<string,array<string,mixed>>
186 */
187 $rules = apply_filters( 'content_control/rule_engine/rules', $rules );
188
189 foreach ( $rules as $rule ) {
190 $this->register_rule( $rule );
191 }
192
193 /**
194 * Allow manipulating registered rules.
195 *
196 * @param Rules $rules Rules instance.
197 *
198 * @return void
199 */
200 do_action( 'content_control/rule_engine/register_rules', $this );
201 }
202
203 /**
204 * Get a list of user rules.
205 *
206 * @return array<string,array<string,mixed>>
207 */
208 protected function get_user_rules() {
209 $verbs = $this->get_verbs();
210 return [
211 'user_is_logged_in' => [
212 'name' => 'user_is_logged_in',
213 'label' => __( 'Logged In', 'content-control' ),
214 'context' => [ 'user' ],
215 'category' => __( 'User', 'content-control' ),
216 'format' => '{category} {verb} {label}',
217 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
218 'callback' => '\is_user_logged_in',
219 ],
220 'user_has_role' => [
221 'name' => 'user_has_role',
222 'label' => __( 'Role(s)', 'content-control' ),
223 'context' => [ 'user' ],
224 'category' => __( 'User', 'content-control' ),
225 'format' => '{category} {verb} {label}',
226 'verbs' => [ $verbs['has'], $verbs['doesnothave'] ],
227 'fields' => [
228 'roles' => [
229 'label' => __( 'Role(s)', 'content-control' ),
230 'type' => 'tokenselect',
231 'multiple' => true,
232 'options' => wp_roles()->get_names(),
233 ],
234 ],
235 'callback' => '\ContentControl\Rules\user_has_role',
236 ],
237 ];
238 }
239
240 /**
241 * Get a list of general content rules.
242 *
243 * @return array<string,array<string,mixed>>
244 */
245 protected function get_general_content_rules() {
246 $rules = [];
247 $verbs = $this->get_verbs();
248
249 $rules['entire_site'] = [
250 'name' => 'entire_site',
251 'label' => __( 'Entire Site (Any Page, Post, or Archive)', 'content-control' ),
252 'context' => [ 'content' ],
253 'category' => __( 'Content', 'content-control' ),
254 'format' => '{category} {verb} {label}',
255 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
256 'callback' => '__return_true',
257 ];
258
259 $rules['content_is_front_page'] = [
260 'name' => 'content_is_front_page',
261 'label' => __( 'The Home Page', 'content-control' ),
262 'context' => [ 'content' ],
263 'category' => __( 'Content', 'content-control' ),
264 'format' => '{category} {verb} {label}',
265 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
266 'callback' => '\ContentControl\Rules\content_is_home_page',
267 ];
268
269 $rules['content_is_blog_index'] = [
270 'name' => 'content_is_blog_index',
271 'label' => __( 'The Blog Index', 'content-control' ),
272 'context' => [ 'content', 'posttype:post' ],
273 'category' => __( 'Content', 'content-control' ),
274 'format' => '{category} {verb} {label}',
275 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
276 'callback' => '\ContentControl\Rules\content_is_blog_index',
277 ];
278
279 $rules['content_is_search_results'] = [
280 'name' => 'content_is_search_results',
281 'label' => __( 'A Search Result Page', 'content-control' ),
282 'context' => [ 'content', 'search' ],
283 'category' => __( 'Content', 'content-control' ),
284 'format' => '{category} {verb} {label}',
285 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
286 'callback' => '\is_search',
287 ];
288
289 $rules['content_is_404_page'] = [
290 'name' => 'content_is_404_page',
291 'label' => __( 'A 404 Error Page', 'content-control' ),
292 'context' => [ 'content', '404' ],
293 'category' => __( 'Content', 'content-control' ),
294 'format' => '{category} {verb} {label}',
295 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
296 'callback' => '\is_404',
297 ];
298
299 return $rules;
300 }
301
302 /**
303 * Get a list of WP post type rules.
304 *
305 * @return array<string,array<string,mixed>>
306 */
307 protected function get_post_type_rules() {
308 $verbs = $this->get_verbs();
309
310 $rules = [];
311 $post_types = get_post_types( [ 'public' => true ], 'objects' );
312
313 foreach ( $post_types as $post_type ) {
314 $type_rules = [];
315 $name = $post_type->name;
316
317 if ( $post_type->has_archive || 'post' === $name ) {
318 $type_rules[ "content_is_{$name}_archive" ] = [
319 'name' => "content_is_{$name}_archive",
320 /* translators: %s: Post type singular name */
321 'label' => sprintf( __( 'A %s Archive', 'content-control' ), $post_type->labels->singular_name ),
322 'callback' => '\ContentControl\Rules\content_is_post_type_archive',
323 ];
324 }
325
326 $type_rules[ "content_is_{$name}" ] = [
327 'name' => "content_is_{$name}",
328 /* translators: %s: Post type singular name */
329 'label' => sprintf( __( 'A %s', 'content-control' ), $post_type->labels->singular_name ),
330 'callback' => '\ContentControl\Rules\content_is_post_type',
331 ];
332
333 $type_rules[ "content_is_selected_{$name}" ] = [
334 'name' => "content_is_selected_{$name}",
335 /* translators: %s: Post type singular name */
336 'label' => sprintf( __( 'A Selected %s', 'content-control' ), $post_type->labels->singular_name ),
337 'fields' => [
338 'selected' => [
339 /* translators: %s: Post type plurals name */
340 'placeholder' => sprintf( __( 'Select %s.', 'content-control' ), strtolower( $post_type->labels->name ) ),
341 'type' => 'postselect',
342 'post_type' => $name,
343 'multiple' => true,
344 ],
345 ],
346 'callback' => '\ContentControl\Rules\content_is_selected_post',
347 ];
348
349 $type_rules[ "content_is_{$name}_with_id" ] = [
350 'name' => "content_is_{$name}_with_id",
351 /* translators: %s: Post type singular name */
352 'label' => sprintf( __( 'A %s with ID', 'content-control' ), $post_type->labels->singular_name ),
353 'fields' => [
354 'selected' => [
355 /* translators: %s: Post type singular name */
356 'placeholder' => sprintf( __( '%s IDs: 128, 129', 'content-control' ), strtolower( $post_type->labels->name ) ),
357 'type' => 'text',
358 ],
359 ],
360 'callback' => '\ContentControl\Rules\content_is_selected_post',
361 ];
362
363 if ( is_post_type_hierarchical( $name ) ) {
364 $type_rules[ "content_is_child_of_{$name}" ] = [
365 'name' => "content_is_child_of_{$name}",
366 /* translators: %s: Post type plural name */
367 'label' => sprintf( __( 'A Child of Selected %s', 'content-control' ), $post_type->labels->name ),
368 'fields' => [
369 'selected' => [
370 /* translators: %s: Post type plural name */
371 'placeholder' => sprintf( __( 'Select %s.', 'content-control' ), strtolower( $post_type->labels->name ) ),
372 'type' => 'postselect',
373 'post_type' => $name,
374 'multiple' => true,
375 ],
376 ],
377 'callback' => '\ContentControl\Rules\content_is_child_of_post',
378 ];
379
380 $type_rules[ "content_is_ancestor_of_{$name}" ] = [
381 'name' => "content_is_ancestor_of_{$name}",
382 /* translators: %s: Post type plural name */
383 'label' => sprintf( __( 'An Ancestor of Selected %s', 'content-control' ), $post_type->labels->name ),
384 'fields' => [
385 'selected' => [
386 /* translators: %s: Post type plural name */
387 'placeholder' => sprintf( __( 'Select %s.', 'content-control' ), strtolower( $post_type->labels->name ) ),
388 'type' => 'postselect',
389 'post_type' => $name,
390 'multiple' => true,
391 ],
392 ],
393 'callback' => '\ContentControl\Rules\content_is_ancestor_of_post',
394 ];
395 }
396
397 $templates = wp_get_theme()->get_page_templates();
398
399 if ( 'page' === $name && ! empty( $templates ) ) {
400 $type_rules[ "content_is_{$name}_with_template" ] = [
401 'name' => "content_is_{$name}_with_template",
402 /* translators: %s: Post type singular name */
403 'label' => sprintf( __( 'A %s With Template', 'content-control' ), $post_type->labels->singular_name ),
404 'fields' => [
405 'selected' => [
406 'type' => 'tokenselect',
407 'multiple' => true,
408 'options' => array_merge( [ 'default' => __( 'Default', 'content-control' ) ], $templates ),
409 ],
410 ],
411 'callback' => '\ContentControl\Rules\content_is_post_with_template',
412 ];
413 }
414
415 foreach ( $type_rules as $rule ) {
416 // Merge defaults.
417 $type_rules[ $rule['name'] ] = wp_parse_args( $rule, [
418 'category' => __( 'Content', 'content-control' ),
419 'context' => [ 'content', "posttype:{$name}" ],
420 'format' => '{category} {verb} {label}',
421 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
422 'fields' => [],
423 'extras' => [
424 'post_type' => $name,
425 ],
426 ] );
427 }
428
429 // Merge type rules & type tax rules.
430 $rules = array_merge( $rules, $type_rules, $this->get_post_type_tax_rules( $name ) );
431 }
432
433 return $rules;
434 }
435
436 /**
437 * Generate post type taxonomy rules.
438 *
439 * @param string $name Post type name.
440 *
441 * @return array<string,array<string,mixed>>
442 */
443 protected function get_post_type_tax_rules( $name ) {
444 $verbs = $this->get_verbs();
445
446 $post_type = get_post_type_object( $name );
447 /**
448 * Taxnomies array.
449 *
450 * @var \WP_Taxonomy[]
451 */
452 $taxonomies = get_object_taxonomies( $name, 'object' );
453 $rules = [];
454
455 foreach ( $taxonomies as $tax_name => $taxonomy ) {
456 $rules[ "content_is_{$name}_with_{$tax_name}" ] = [
457 'name' => "content_is_{$name}_with_{$tax_name}",
458 'label' => sprintf(
459 /* translators: %1$s: Post type singular name, %2$s: Taxonomy singular name */
460 _x( 'A %1$s with %2$s', 'condition: post type plural and taxonomy singular label ie. A Post With Category', 'content-control' ),
461 $post_type->labels->singular_name,
462 $taxonomy->labels->singular_name
463 ),
464 'context' => [ 'content', "posttype:{$name}", "taxonomy:{$tax_name}" ],
465 'category' => __( 'Content', 'content-control' ),
466 'format' => '{category} {verb} {label}',
467 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
468 'fields' => [
469 'selected' => [
470 'placeholder' => sprintf(
471 /* translators: %s: Taxonomy singular name */
472 _x( 'Select %s.', 'condition: post type plural label ie. Select categories', 'content-control' ),
473 strtolower( $taxonomy->labels->name )
474 ),
475 'type' => 'taxonomyselect',
476 'taxonomy' => $tax_name,
477 'multiple' => true,
478 ],
479 ],
480 'extras' => [
481 'post_type' => $name,
482 'taxonomy' => $tax_name,
483 ],
484 'callback' => '\ContentControl\Rules\content_is_post_with_tax_term',
485 ];
486 }
487
488 return $rules;
489 }
490
491 /**
492 * Generates conditions for all public taxonomies.
493 *
494 * @return array<string,array<string,mixed>>
495 */
496 protected function get_taxonomy_rules() {
497 $rules = [];
498 $taxonomies = get_taxonomies( [ 'public' => true ], 'objects' );
499 $verbs = $this->get_verbs();
500
501 foreach ( $taxonomies as $tax_name => $taxonomy ) {
502 $tax_defaults = [
503 'category' => __( 'Content', 'content-control' ),
504 'context' => [ 'content', "taxonomy:{$tax_name}" ],
505 'format' => '{category} {verb} {label}',
506 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
507 'fields' => [],
508 'extras' => [
509 'taxonomy' => $tax_name,
510 ],
511 ];
512
513 $rules[ "content_is_{$tax_name}_archive" ] = wp_parse_args( [
514 'name' => "content_is_{$tax_name}_archive",
515 /* translators: %s: Taxonomy plural name */
516 'label' => sprintf( _x( 'A %s Archive', 'condition: taxonomy plural label ie. A Category Archive', 'content-control' ), $taxonomy->labels->singular_name ),
517 'callback' => '\ContentControl\Rules\content_is_taxonomy_archive',
518 ], $tax_defaults );
519
520 $rules[ "content_is_selected_tax_{$tax_name}" ] = wp_parse_args( [
521 'name' => "content_is_selected_tax_{$tax_name}",
522 /* translators: %s: Taxonomy plural name */
523 'label' => sprintf( _x( 'A Selected %s', 'condition: taxonomy plural label ie. A Selected Category', 'content-control' ), $taxonomy->labels->singular_name ),
524 'fields' => [
525 'selected' => [
526 /* translators: %s: Taxonomy plural name */
527 'placeholder' => sprintf( _x( 'Select %s.', 'condition: taxonomy plural label ie. Select Categories', 'content-control' ), strtolower( $taxonomy->labels->name ) ),
528 'type' => 'taxonomyselect',
529 'taxonomy' => $tax_name,
530 'multiple' => true,
531 ],
532 ],
533 'callback' => '\ContentControl\Rules\content_is_selected_term',
534 ], $tax_defaults );
535
536 $rules[ "content_is_tax_{$tax_name}_with_id" ] = wp_parse_args( [
537 'name' => "content_is_tax_{$tax_name}_with_id",
538 /* translators: %s: Taxonomy plural name */
539 'label' => sprintf( _x( 'A %s with ID', 'condition: taxonomy plural label ie. A Category with ID: Selected', 'content-control' ), $taxonomy->labels->name ),
540 'fields' => [
541 'selected' => [
542 /* translators: %s: Taxonomy plural name */
543 'placeholder' => sprintf( _x( '%s IDs: 128, 129', 'condition: taxonomy plural label ie. Category IDs', 'content-control' ), strtolower( $taxonomy->labels->singular_name ) ),
544 'type' => 'text',
545 ],
546 ],
547 'callback' => '\ContentControl\Rules\content_is_selected_term',
548 ], $tax_defaults );
549 }
550
551 return $rules;
552 }
553
554 /**
555 * Get an array of rule default values.
556 *
557 * @return array<string,mixed> Array of rule default values.
558 */
559 public function get_rule_defaults() {
560 $verbs = $this->get_verbs();
561 return [
562 'name' => '',
563 'label' => '',
564 'context' => [],
565 'category' => __( 'Content', 'content-control' ),
566 'format' => '{category} {verb} {label}',
567 'verbs' => [ $verbs['is'], $verbs['isnot'] ],
568 'fields' => [],
569 'callback' => null,
570 'frontend' => false,
571 ];
572 }
573
574 /**
575 * Register & remap deprecated conditions to rules.
576 *
577 * @return void
578 */
579 protected function register_deprecated_rules() {
580 /**
581 * Filters the old conditions to be registered as rules.
582 *
583 * @deprecated 2.0.0
584 *
585 * @param array $old_rules Array of old rules to manipulate.
586 *
587 * @return array
588 */
589 $old_rules = apply_filters( 'content_control/rule_engine/deprecated_rules', [] );
590
591 if ( ! empty( $old_rules ) ) {
592 $old_rules = $this->parse_old_rules( $old_rules );
593
594 foreach ( $old_rules as $rule ) {
595 $rule['deprecated'] = true;
596 $this->register_rule( $rule );
597 }
598 }
599 }
600
601 /**
602 * Parse rules that are still registered using the older deprecated methods.
603 *
604 * @param array<string,mixed> $old_rules Array of old rules to manipulate.
605 * @return array<string,mixed>
606 */
607 public function parse_old_rules( $old_rules ) {
608 $new_rules = [];
609
610 foreach ( $old_rules as $key => $old_rule ) {
611 $new_rules[ $key ] = $this->remap_old_rule( $old_rule );
612 }
613
614 return $new_rules;
615 }
616
617 /**
618 * Remaps keys & values from an old `condition` into a new `rule`.
619 *
620 * @param array<string,mixed> $old_rule Old rule definition.
621 * @return array<string,mixed> New rule definition.
622 */
623 public function remap_old_rule( $old_rule ) {
624 $old_rule = wp_parse_args( $old_rule, $this->get_old_rule_defaults() );
625
626 $new_rule = [
627 'format' => '{label}',
628 ];
629
630 $remaped_keys = [
631 'id' => 'name',
632 'name' => 'label',
633 'group' => 'category',
634 'fields' => 'fields',
635 'callback' => 'callback',
636 'advanced' => 'frontend',
637 'priority' => 'priority',
638 ];
639
640 foreach ( $remaped_keys as $old_key => $new_key ) {
641 if ( isset( $old_rule[ $old_key ] ) ) {
642 $new_rule[ $new_key ] = $old_rule[ $old_key ];
643 unset( $old_rule[ $old_key ] );
644 }
645 }
646
647 // Merge any leftover 'unknonw' keys, with new stuff second.
648 return array_merge( $new_rule, $old_rule );
649 }
650
651 /**
652 * Get an array of old rule default values.
653 *
654 * @return array<string,mixed> Array of old rule default values.
655 */
656 private function get_old_rule_defaults() {
657 return [
658 'id' => '',
659 'callback' => null,
660 'group' => '',
661 'name' => '',
662 'priority' => 10,
663 'fields' => [],
664 'advanced' => false,
665 ];
666 }
667 }
668