PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / 2.6.1
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More v2.6.1
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 / inc / functions / query.php

query.php in Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More 2.6.1, at inc/functions/query.php

568 lines 13.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Query functions.
4 *
5 * @package ContentControl
6 */
7
8 namespace ContentControl;
9
10 defined( 'ABSPATH' ) || exit;
11
12 /**
13 * Get the main query.
14 *
15 * @return \WP_Query|null
16 */
17 function get_main_wp_query() {
18 global $wp_the_query;
19
20 if ( ! is_null( $wp_the_query ) ) {
21 /**
22 * WP Query object.
23 *
24 * @var \WP_Query $wp_the_query
25 */
26 return $wp_the_query;
27 }
28
29 return null;
30 }
31
32 /**
33 * Get the current wp query.
34 *
35 * Helper that returns the current query object, reguardless of if
36 * it's the main query or not.
37 *
38 * @return \WP_Query|null
39 */
40 function get_current_wp_query() {
41 global $wp_query;
42
43 if ( ! is_null( $wp_query ) ) {
44 /**
45 * WP Query object.
46 *
47 * @var \WP_Query $wp_query
48 */
49 return $wp_query;
50 }
51
52 return get_main_wp_query();
53 }
54
55 /**
56 * Get the current query.
57 *
58 * @param \WP_Query|\WP_Term_Query|null $query Query object.
59 *
60 * @return \WP_Query|\WP_Term_Query|null
61 */
62 function get_query( $query = null ) {
63 if ( is_null( $query ) ) {
64 if ( ! global_is_empty( 'current_query' ) ) {
65 /**
66 * WP Query object.
67 *
68 * @var \WP_Query|\WP_Term_Query $query
69 */
70 $query = get_global( 'current_query' );
71 } else {
72 $query = get_current_wp_query();
73 }
74 }
75
76 return $query;
77 }
78
79 /**
80 * Set the current query context.
81 *
82 * @param string $context 'main', 'main/posts', 'posts', 'main/blocks', 'blocks`.
83 *
84 * @return void
85 */
86 function override_query_context( $context ) {
87 set_global( 'current_query_context', $context );
88 }
89
90 /**
91 * Reset the current query context.
92 *
93 * @return void
94 */
95 function reset_query_context() {
96 reset_global( 'current_query_context' );
97 }
98
99 /**
100 * Get or set the current rule context (globaly accessible).
101 *
102 * 'main', 'main/posts', 'posts', 'main/blocks', 'blocks`
103 *
104 * Rules can work differently depending on the context they are being checked in.
105 * This context allows us to handle the main query differently to other queries,
106 * and blocks. It further allows us to handle blocks in several unique ways per
107 * rule.
108 *
109 * 1. Main query is checked in the template_redirect action.
110 * 2. Main query posts are checked in the the_posts filter & $wp_query->is_main_query().
111 * 3. Alternate query posts are checked in the_posts or pre_get_posts & ! $wp_query->is_main_query().
112 * 4. Blocks are checked in the content_control/should_hide_block filter.
113 *
114 * switch ( current_query_context() ) {
115 * // Catch all known contexts.
116 * case 'main':
117 * case 'main/blocks':
118 * case 'main/posts':
119 * case 'posts':
120 * case 'blocks':
121 * case 'restapi/posts':
122 * case 'restapi':
123 * case 'restapi/terms':
124 * case 'terms':
125 * case 'unknown':
126 * return false;
127 * }
128 *
129 * @param \WP_Query|null $query Query object.
130 *
131 * @return 'main'|'main/posts'|'posts'|'main/blocks'|'blocks'|'restapi'|'restapi/posts'|'restapi/terms'|'terms'|'unknown'
132 */
133 function current_query_context( $query = null ) {
134 if ( ! global_is_empty( 'current_query_context' ) ) {
135 return get_global( 'current_query_context' );
136 }
137
138 $query = get_query( $query );
139 $is_main = is_a( $query, '\WP_Query' ) && $query->is_main_query();
140
141 // Blocks in the main page or other locations.
142 if ( doing_filter( 'content_control/should_hide_block' ) ) {
143 return $is_main ? 'main/blocks' : 'blocks';
144 }
145
146 // Main query (page/psst/home/search/archive etc) (template_redirect).
147 if ( $is_main && doing_action( 'template_redirect' ) ) {
148 return 'main';
149 }
150
151 // Before we process plain queries, we need to check if we're in a REST API request.
152 if ( is_rest() ) {
153 if ( doing_filter( 'get_terms' ) ) {
154 return 'restapi/terms';
155 }
156
157 if ( doing_filter( 'pre_get_posts' ) || doing_filter( 'the_posts' ) ) {
158 return 'restapi/posts';
159 }
160
161 return 'restapi';
162 }
163
164 // Process plain queries.
165 if ( doing_filter( 'get_terms' ) ) {
166 return 'terms';
167 }
168
169 if ( doing_filter( 'pre_get_posts' ) || doing_filter( 'the_posts' ) ) {
170 return $is_main ? 'main/posts' : 'posts';
171 }
172
173 // Default to posts.
174 return 'posts';
175 }
176
177 /**
178 * Set the current rule (globaly accessible).
179 *
180 * Because we check posts in `the_posts`, we can't trust the global $wp_query
181 * has been set yet, so we need to manage global state ourselves.
182 *
183 * @param \WP_Query|\WP_Term_Query|null $query WP_Query object.
184 *
185 * @return void
186 */
187 function set_rules_query( $query ) {
188 set_global( 'current_query', $query );
189 }
190
191 /**
192 * Setup the current content.
193 *
194 * @param int|\WP_Post|\WP_Term|null $content_id Content ID.
195 *
196 * @return bool
197 *
198 * @since 2.4.0 - Added support for `terms` context.
199 */
200 function setup_content_globals( $content_id ) {
201 // Return early if we don't have a post ID.
202 if ( is_null( $content_id ) ) {
203 return false;
204 }
205
206 switch ( current_query_context() ) {
207 case 'terms':
208 case 'restapi/terms':
209 return setup_term_globals( $content_id );
210 default:
211 return setup_post_globals( $content_id );
212 }
213 }
214
215 /**
216 * Check and overload global post if needed.
217 *
218 * This has no effect when checking global queries ($post_id = null).
219 *
220 * @param int|\WP_Post|null $post_id Post ID.
221 *
222 * @return bool
223 *
224 * @since 2.4.0 - Added support for `terms` context.
225 */
226 function setup_post_globals( $post_id = null ) {
227 global $post;
228
229 // Return early if we don't have a post ID.
230 if ( is_null( $post_id ) ) {
231 return false;
232 }
233
234 // Set current post id based on global $post.
235 $current_post_id = $post->ID ?? null;
236
237 // Check if we should overload the post. This means its not the current post.
238 $overload_post =
239 // If we have a post ID, check if it's different from the current post ID.
240 ( is_object( $post_id ) && $post_id->ID !== $current_post_id ) ||
241 // If we have an int, check if it's different from the current post ID.
242 ( is_int( $post_id ) && $post_id !== $current_post_id );
243
244 if ( $overload_post ) {
245 // Push the current $post to the stack so we can restore it later.
246 push_to_global( 'overloaded_posts', $current_post_id );
247
248 // Overload the globals so conditionals work properly.
249 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
250 $post = get_post( $post_id );
251 setup_postdata( $post );
252 }
253
254 return $overload_post;
255 }
256
257 /**
258 * Check and overload global term if needed.
259 *
260 * This has no effect when checking global queries ($term_id = null).
261 *
262 * @param int|\WP_Term|null $term_id Term ID.
263 *
264 * @return bool
265 *
266 * @since 2.4.0 - Added support for `terms` context.
267 */
268 function setup_term_globals( $term_id = null ) {
269 global $cc_term; // Backward compatibility.
270
271 $current_term = get_global( 'term' ); // Used instead of global $cc_term.
272
273 // Return early if we don't have a term ID.
274 if ( is_null( $term_id ) ) {
275 return false;
276 }
277
278 // Set current term id based on global $current_term.
279 $current_term_id = $current_term->term_id ?? null;
280
281 // Check if we should overload the term.
282 $overload_term =
283 // If we have a term ID, check if it's different from the current term ID.
284 ( is_object( $term_id ) && $term_id->term_id !== $current_term_id ) ||
285 // If we have an int, check if it's different from the current term ID.
286 ( is_int( $term_id ) && $term_id !== $current_term_id );
287
288 if ( $overload_term ) {
289 // Store only the ID of the current term for later restoration.
290 push_to_global( 'overloaded_terms', $current_term_id );
291
292 // Overload the globals so conditionals work properly.
293 $cc_term = get_term( $term_id );
294 // Set the global term object (forward compatibility).
295 set_global( 'term', $cc_term );
296 }
297
298 return $overload_term;
299 }
300
301 /**
302 * Setup the current content.
303 *
304 * @return void
305 *
306 * @since 2.4.0 - Added support for `terms` context.
307 */
308 function reset_content_globals() {
309 switch ( current_query_context() ) {
310 case 'terms':
311 case 'restapi/terms':
312 reset_term_globals();
313 break;
314 default:
315 reset_post_globals();
316 break;
317 }
318 }
319
320 /**
321 * Check and clear global post if needed.
322 *
323 * @global \WP_Post $post
324 *
325 * @return void
326 *
327 * @since 2.4.0 - Added support for `terms` context.
328 */
329 function reset_post_globals() {
330 if ( global_is_empty( 'overloaded_posts' ) ) {
331 return;
332 }
333
334 global $post;
335
336 $stored_post_id = pop_from_global( 'overloaded_posts' );
337 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
338 $post = get_post( $stored_post_id );
339 // Reset global post object.
340 setup_postdata( $post );
341 }
342
343 /**
344 * Check and clear global term if needed.
345 *
346 * @return void
347 *
348 * @since 2.4.0 - Added support for `terms` context.
349 */
350 function reset_term_globals() {
351 if ( global_is_empty( 'overloaded_terms' ) ) {
352 // Reset global term object since it never really existed.
353 reset_global( 'term' );
354 return;
355 }
356
357 global $cc_term; // Backward compatibility.
358
359 $stored_term_id = pop_from_global( 'overloaded_terms' );
360 // Reset global post object.
361 $cc_term = get_term( $stored_term_id );
362 set_global( 'term', $cc_term );
363 }
364
365 /**
366 * Get the content ID for the current query item (post, term, etc).
367 *
368 * @return int|null
369 */
370 function get_the_content_id() {
371 $context = current_query_context();
372
373 switch ( $context ) {
374 case 'terms':
375 case 'restapi/terms':
376 $term = get_global( 'term' ); // Used instead of global $cc_term.
377 return $term->term_id ?? null;
378
379 default:
380 return get_the_ID();
381 }
382 }
383
384 /**
385 * Set up the post globals.
386 *
387 * @param int|\WP_Post|null $post_id Post ID.
388 *
389 * @return boolean
390 *
391 * @deprecated 2.4.0 - Use `setup_content_globals() or `setup_post_globals()` instead.
392 */
393 function setup_post( $post_id = null ) {
394 return setup_content_globals( $post_id );
395 }
396
397 /**
398 * Set up the term globals.
399 *
400 * @param int|\WP_Term|null $term_id Term ID.
401 *
402 * @return boolean
403 *
404 * @deprecated 2.4.0 - Use `setup_term_globals()` instead.
405 */
406 function setup_term_object( $term_id = null ) {
407 return setup_term_globals( $term_id );
408 }
409
410 /**
411 * Check and clear global post if needed.
412 *
413 * @global \WP_Post $post
414 *
415 * @return void
416 *
417 * @deprecated 2.4.0 - Use `reset_post_globals()` instead.
418 */
419 function reset_post() {
420 reset_post_globals();
421 }
422
423 /**
424 * Check and clear global term if needed.
425 *
426 * @return void
427 *
428 * @deprecated 2.4.0 - Use `reset_term_globals()` instead.
429 */
430 function reset_term_object() {
431 reset_term_globals();
432 }
433
434 /**
435 * Get the endpoints for a registered post types.
436 *
437 * @since 2.2.0
438 * @since 2.5.0 Use `rest_get_route_for_post_type_items()` instead of `get_post_type_object()->rest_base`.
439 *
440 * @return array<string,string>
441 */
442 function get_post_type_endpoints() {
443 $endpoints = [];
444 $post_types = get_post_types();
445
446 foreach ( $post_types as $post_type ) {
447 $endpoint = rest_get_route_for_post_type_items( $post_type );
448
449 // Possible if show_in_rest is false or if the post type is not registered.
450 if ( '' === $endpoint ) {
451 $object = get_post_type_object( $post_type );
452
453 if ( ! $object ) {
454 continue;
455 }
456
457 $endpoint = "/wp/v2/{$object->rest_base}";
458 }
459
460 $endpoints[ $endpoint ] = $post_type;
461 }
462
463 return $endpoints;
464 }
465
466 /**
467 * Get the endpoints for a registered taxonomies.
468 *
469 * @since 2.2.0
470 * @since 2.5.0 Use `rest_get_route_for_taxonomy_items()` instead of `get_taxonomy_object()->rest_base`.
471 *
472 * @return array<string,string>
473 */
474 function get_taxonomy_endpoints() {
475 $endpoints = [];
476 $taxonomies = get_taxonomies();
477
478 foreach ( $taxonomies as $taxonomy ) {
479 $endpoint = rest_get_route_for_taxonomy_items( $taxonomy );
480
481 // Possible if show_in_rest is false or if the taxonomy is not registered.
482 if ( '' === $endpoint ) {
483 $object = get_taxonomy( $taxonomy );
484
485 if ( ! $object ) {
486 continue;
487 }
488
489 $endpoint = "/wp/v2/{$object->rest_base}";
490 }
491
492 $endpoints[ $endpoint ] = $taxonomy;
493 }
494
495 return $endpoints;
496 }
497
498 /**
499 * Get the intent of the current REST API request.
500 *
501 * @since 2.2.0
502 * @since 2.3.0 - Added filter to allow overriding the intent.
503 * @since 2.4.0 - Added second paramter to the`content_control/get_rest_api_intent` filter pass the `$rest_route`.
504 *
505 * @return array{type:'post_type'|'taxonomy'|'unknown',name:string,id:int,index:bool,search:string|false}
506 */
507 function get_rest_api_intent() {
508 global $wp;
509
510 $intent = get_global( 'rest_intent' );
511
512 $rest_route = null;
513
514 if ( is_null( $intent ) ) {
515 $intent = [
516 'type' => 'unknown',
517 'name' => '',
518 'id' => 0,
519 'index' => false,
520 'search' => false,
521 ];
522
523 // Handle built-in REST API endpoints.
524 if ( ! empty( $wp->query_vars['rest_route'] ) ) {
525 $rest_route = $wp->query_vars['rest_route'];
526
527 if ( strpos( $rest_route, '/wp/v2/' ) === 0 ) {
528 $post_type_endpoints = get_post_type_endpoints();
529 $taxonomy_endpoints = get_taxonomy_endpoints();
530
531 $endpoint_parts = explode( '/', str_replace( '/wp/v2/', '', $rest_route ) );
532
533 // If we have a post type or taxonomy, the name is the first part (posts, categories).
534 $intent['name'] = sanitize_key( $endpoint_parts[0] );
535 // Check if this is a search request.
536 $intent['search'] = isset( $wp->query_vars['search'] ) ? sanitize_title( $wp->query_vars['search'] ) : false;
537
538 if ( count( $endpoint_parts ) > 1 ) {
539 // If we have an ID, then the second part is the ID.
540 $intent['id'] = absint( $endpoint_parts[1] );
541 } else {
542 // If we have no ID, then we are either searching or indexing.
543 $intent['index'] = true;
544 }
545
546 // Build a matching route.
547 $endpoint_route = "/wp/v2/{$intent['name']}";
548
549 if ( isset( $post_type_endpoints[ $endpoint_route ] ) ) {
550 $intent['type'] = 'post_type';
551 } elseif ( isset( $taxonomy_endpoints[ $endpoint_route ] ) ) {
552 $intent['type'] = 'taxonomy';
553 } elseif ( 'search' === $intent['name'] ) {
554 $intent['type'] = 'search';
555 }
556 } else {
557 // We currently have no way of really dealing with non WP REST requests.
558 // This filter allows us or others to correctly handle these requests in the future.
559 apply_filters( 'content_control/determine_uknonwn_rest_api_intent', $intent, $rest_route );
560 }
561 }
562
563 set_global( 'rest_intent', $intent );
564 }
565
566 return apply_filters( 'content_control/get_rest_api_intent', $intent, $rest_route );
567 }
568