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
← All changes | inc/functions/query.php +395 -32 2.0.42.6.1 View file →
@@ -54,23 +54,21 @@
54 54
55 55 /**
56 56 * Get the current query.
57 57 *
58 - * @param \WP_Query|null $query Query object.
58 + * @param \WP_Query|\WP_Term_Query|null $query Query object.
59 59 *
60 - * @return \WP_Query
60 + * @return \WP_Query|\WP_Term_Query|null
61 61 */
62 62 function get_query( $query = null ) {
63 - global $cc_current_query;
64 -
65 63 if ( is_null( $query ) ) {
66 - if ( isset( $cc_current_query ) && ! is_null( $cc_current_query ) ) {
64 + if ( ! global_is_empty( 'current_query' ) ) {
67 65 /**
68 66 * WP Query object.
69 67 *
70 - * @var \WP_Query $query
68 + * @var \WP_Query|\WP_Term_Query $query
71 69 */
72 - $query = $cc_current_query;
70 + $query = get_global( 'current_query' );
73 71 } else {
74 72 $query = get_current_wp_query();
75 73 }
76 74 }
@@ -81,24 +79,26 @@
81 79 /**
82 80 * Set the current query context.
83 81 *
84 82 * @param string $context 'main', 'main/posts', 'posts', 'main/blocks', 'blocks`.
83 + *
84 + * @return void
85 85 */
86 86 function override_query_context( $context ) {
87 - global $cc_current_query_context;
88 - $cc_current_query_context = $context;
87 + set_global( 'current_query_context', $context );
89 88 }
90 89
91 90 /**
92 91 * Reset the current query context.
92 + *
93 + * @return void
93 94 */
94 95 function reset_query_context() {
95 - global $cc_current_query_context;
96 - unset( $cc_current_query_context );
96 + reset_global( 'current_query_context' );
97 97 }
98 98
99 99 /**
100 - * Get or set the current rule (globaly accessible).
100 + * Get or set the current rule context (globaly accessible).
101 101 *
102 102 * 'main', 'main/posts', 'posts', 'main/blocks', 'blocks`
103 103 *
104 104 * Rules can work differently depending on the context they are being checked in.
@@ -110,21 +110,34 @@
110 110 * 2. Main query posts are checked in the the_posts filter & $wp_query->is_main_query().
111 111 * 3. Alternate query posts are checked in the_posts or pre_get_posts & ! $wp_query->is_main_query().
112 112 * 4. Blocks are checked in the content_control/should_hide_block filter.
113 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 + *
114 129 * @param \WP_Query|null $query Query object.
115 130 *
116 - * @return string 'main', 'main/posts', 'posts', 'main/blocks', 'blocks`.
131 + * @return 'main'|'main/posts'|'posts'|'main/blocks'|'blocks'|'restapi'|'restapi/posts'|'restapi/terms'|'terms'|'unknown'
117 132 */
118 133 function current_query_context( $query = null ) {
119 - global $cc_current_query_context;
120 -
121 - if ( isset( $cc_current_query_context ) ) {
122 - return $cc_current_query_context;
134 + if ( ! global_is_empty( 'current_query_context' ) ) {
135 + return get_global( 'current_query_context' );
123 136 }
124 137
125 138 $query = get_query( $query );
126 - $is_main = $query->is_main_query();
139 + $is_main = is_a( $query, '\WP_Query' ) && $query->is_main_query();
127 140
128 141 // Blocks in the main page or other locations.
129 142 if ( doing_filter( 'content_control/should_hide_block' ) ) {
130 143 return $is_main ? 'main/blocks' : 'blocks';
@@ -134,8 +147,26 @@
134 147 if ( $is_main && doing_action( 'template_redirect' ) ) {
135 148 return 'main';
136 149 }
137 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 +
138 169 if ( doing_filter( 'pre_get_posts' ) || doing_filter( 'the_posts' ) ) {
139 170 return $is_main ? 'main/posts' : 'posts';
140 171 }
141 172
@@ -148,40 +179,74 @@
148 179 *
149 180 * Because we check posts in `the_posts`, we can't trust the global $wp_query
150 181 * has been set yet, so we need to manage global state ourselves.
151 182 *
152 - * @param string $query WP_Query object.
183 + * @param \WP_Query|\WP_Term_Query|null $query WP_Query object.
153 184 *
154 185 * @return void
155 186 */
156 187 function set_rules_query( $query ) {
157 - global $cc_current_query;
158 - $cc_current_query = $query;
188 + set_global( 'current_query', $query );
159 189 }
160 190
161 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 +/**
162 216 * Check and overload global post if needed.
163 217 *
164 218 * This has no effect when checking global queries ($post_id = null).
165 219 *
166 - * @param int|null $post_id Post ID.
220 + * @param int|\WP_Post|null $post_id Post ID.
167 221 *
168 222 * @return bool
223 + *
224 + * @since 2.4.0 - Added support for `terms` context.
169 225 */
170 -function setup_post( $post_id = null ) {
226 +function setup_post_globals( $post_id = null ) {
171 227 global $post;
172 228
229 + // Return early if we don't have a post ID.
173 230 if ( is_null( $post_id ) ) {
174 231 return false;
175 232 }
176 233
177 - $current_post_id = isset( $post ) ? $post->ID : null;
234 + // Set current post id based on global $post.
235 + $current_post_id = $post->ID ?? null;
178 236
179 - $overload_post = isset( $post_id ) && (
180 - ( is_object( $post_id ) && $post_id->ID !== $current_post_id ) ||
181 - ( is_int( $post_id ) && $post_id !== $current_post_id ) );
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 );
182 243
183 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.
184 249 // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
185 250 $post = get_post( $post_id );
186 251 setup_postdata( $post );
187 252 }
@@ -189,16 +254,314 @@
189 254 return $overload_post;
190 255 }
191 256
192 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 +/**
193 321 * Check and clear global post if needed.
194 322 *
195 - * @param bool $overload_post Whether post was overloaded.
323 + * @global \WP_Post $post
196 324 *
197 325 * @return void
326 + *
327 + * @since 2.4.0 - Added support for `terms` context.
198 328 */
199 -function clear_post( $overload_post = false ) {
200 - if ( $overload_post ) {
201 - // Reset global post object.
202 - wp_reset_postdata();
329 +function reset_post_globals() {
330 + if ( global_is_empty( 'overloaded_posts' ) ) {
331 + return;
203 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 );
204 567 }