PluginProbe
WPSSO Core – Complete Schema Markup and Meta Tags / 22.7.0
WPSSO Core – Complete Schema Markup and Meta Tags v22.7.0
22.7.0 22.6.1 22.6.0 22.5.3 22.5.2 22.5.1 22.4.0 22.3.0 22.2.1 22.2.0 22.1.3 22.1.2 22.1.1 22.1.0 22.0.0 21.13.3 21.13.2 trunk 21.13.1
← All changes | lib/term.php +996 -0 21.13.122.7.0 View file →
@@ -1,0 +1,996 @@
1 +<?php
2 +/*
3 + * License: GPLv3
4 + * License URI: https://www.gnu.org/licenses/gpl.txt
5 + * Copyright 2012-2026 Jean-Sebastien Morisset (https://wpsso.com/)
6 + */
7 +
8 +if ( ! defined( 'ABSPATH' ) ) {
9 +
10 + die( 'These aren\'t the droids you\'re looking for.' );
11 +}
12 +
13 +if ( ! defined( 'WPSSO_PLUGINDIR' ) ) {
14 +
15 + die( 'Do. Or do not. There is no try.' );
16 +}
17 +
18 +if ( ! class_exists( 'WpssoAbstractWpMeta' ) ) {
19 +
20 + require_once WPSSO_PLUGINDIR . 'lib/abstract/wp-meta.php';
21 +}
22 +
23 +if ( ! class_exists( 'WpssoTerm' ) ) {
24 +
25 + class WpssoTerm extends WpssoAbstractWpMeta {
26 +
27 + private $query_term_id = 0;
28 + private $query_tax_slug = '';
29 +
30 + public function __construct( &$plugin ) {
31 +
32 + $this->p =& $plugin;
33 +
34 + if ( $this->p->debug->enabled ) {
35 +
36 + $this->p->debug->mark();
37 + }
38 +
39 + /*
40 + * This hook is fired once WordPress, plugins, and the theme are fully loaded and instantiated.
41 + */
42 + add_action( 'wp_loaded', array( $this, 'add_wp_callbacks' ) );
43 + }
44 +
45 + /*
46 + * Add WordPress action and filter callbacks.
47 + */
48 + public function add_wp_callbacks() {
49 +
50 + if ( $this->p->debug->enabled ) {
51 +
52 + $this->p->debug->mark();
53 + }
54 +
55 + /*
56 + * Since WPSSO Core v17.0.0.
57 + *
58 + * Register our term meta.
59 + */
60 + $this->register_meta( $object_type = 'term', WPSSO_META_NAME );
61 +
62 + $is_admin = is_admin(); // Only check once.
63 +
64 + if ( $is_admin ) {
65 +
66 + /*
67 + * Hook a minimum number of admin actions to maximize performance. The taxonomy and tag_ID
68 + * arguments are always present when we're editing a category and/or tag page, so return
69 + * immediately if they're not present.
70 + */
71 + if ( ( $this->query_tax_slug = SucomUtil::get_request_value( 'taxonomy' ) ) === '' ) { // Uses sanitize_text_field.
72 +
73 + if ( $this->p->debug->enabled ) {
74 +
75 + $this->p->debug->log( 'exiting early: no taxonomy query argument' );
76 + }
77 +
78 + return;
79 + }
80 +
81 + if ( $this->p->debug->enabled ) {
82 +
83 + $this->p->debug->log( 'query tax slug = ' . $this->query_tax_slug );
84 + }
85 +
86 + /*
87 + * Add edit table columns.
88 + */
89 + if ( $this->p->debug->enabled ) {
90 +
91 + $this->p->debug->log( 'adding column filters for taxonomy ' . $this->query_tax_slug );
92 + }
93 +
94 + add_filter( 'manage_edit-' . $this->query_tax_slug . '_columns', array( $this, 'add_term_column_headings' ), WPSSO_ADD_COLUMN_PRIORITY, 1 );
95 + add_filter( 'manage_edit-' . $this->query_tax_slug . '_sortable_columns', array( $this, 'add_sortable_columns' ), 10, 1 );
96 + add_filter( 'manage_' . $this->query_tax_slug . '_custom_column', array( $this, 'get_column_content' ), 10, 3 );
97 +
98 + /*
99 + * The 'parse_query' action is hooked once in the WpssoPost class to set the column orderby for
100 + * post, term, and user edit tables.
101 + *
102 + * This comment is here as a reminder - do not uncomment the following 'parse_query' action hook.
103 + *
104 + * add_action( 'parse_query', array( $this, 'set_column_orderby' ), 10, 1 );
105 + */
106 +
107 + /*
108 + * Maybe create or update the term column content.
109 + */
110 + add_filter( 'get_term_metadata', array( $this, 'check_sortable_meta' ), 1000, 4 );
111 +
112 + if ( ( $this->query_term_id = SucomUtil::get_request_value( 'tag_ID' ) ) === '' ) { // Uses sanitize_text_field.
113 +
114 + return;
115 + }
116 +
117 + if ( $this->p->debug->enabled ) {
118 +
119 + $this->p->debug->log( 'query term_id = ' . $this->query_term_id );
120 + }
121 +
122 + /*
123 + * Available taxonomy and term actions:
124 + *
125 + * do_action( "create_$taxonomy", $term_id, $tt_id );
126 + * do_action( "created_$taxonomy", $term_id, $tt_id );
127 + * do_action( "edited_$taxonomy", $term_id, $tt_id );
128 + * do_action( "delete_$taxonomy", $term_id, $tt_id, $deleted_term );
129 + *
130 + * do_action( "create_term", $term_id, $tt_id, $taxonomy );
131 + * do_action( "created_term", $term_id, $tt_id, $taxonomy );
132 + * do_action( "edited_term", $term_id, $tt_id, $taxonomy );
133 + * do_action( 'delete_term', $term_id, $tt_id, $taxonomy, $deleted_term );
134 + */
135 + if ( ! empty( $_GET ) ) { // Skip some action hooks if no query argument(s).
136 +
137 + /*
138 + * load_meta_page() priorities: 100 post, 200 user, 300 term
139 + *
140 + * Sets the parent::$head_tags and parent::$head_info class properties.
141 + */
142 + add_action( 'current_screen', array( $this, 'load_meta_page' ), 300, 1 );
143 +
144 + add_action( $this->query_tax_slug . '_pre_edit_form', array( $this, 'add_meta_boxes' ), 10, 2 );
145 + add_action( $this->query_tax_slug . '_edit_form', array( $this, 'show_metaboxes' ), -100, 2 );
146 + }
147 +
148 + add_action( 'created_' . $this->query_tax_slug, array( $this, 'save_options' ), WPSSO_META_SAVE_PRIORITY, 2 );
149 + add_action( 'created_' . $this->query_tax_slug, array( $this, 'clear_cache' ), WPSSO_META_CLEAR_PRIORITY, 2 );
150 + add_action( 'created_' . $this->query_tax_slug, array( $this, 'refresh_cache' ), WPSSO_META_REFRESH_PRIORITY, 2 );
151 +
152 + add_action( 'edited_' . $this->query_tax_slug, array( $this, 'save_options' ), WPSSO_META_SAVE_PRIORITY, 2 );
153 + add_action( 'edited_' . $this->query_tax_slug, array( $this, 'clear_cache' ), WPSSO_META_CLEAR_PRIORITY, 2 );
154 + add_action( 'edited_' . $this->query_tax_slug, array( $this, 'refresh_cache' ), WPSSO_META_REFRESH_PRIORITY, 2 );
155 +
156 + add_action( 'delete_' . $this->query_tax_slug, array( $this, 'delete_options' ), WPSSO_META_SAVE_PRIORITY, 2 );
157 + add_action( 'delete_' . $this->query_tax_slug, array( $this, 'clear_cache' ), WPSSO_META_CLEAR_PRIORITY, 2 );
158 + }
159 + }
160 +
161 + /*
162 + * Get the $mod object for a term id.
163 + */
164 + public function get_mod( $term_id, $tax_slug = '' ) {
165 +
166 + if ( $this->p->debug->enabled ) {
167 +
168 + $this->p->debug->mark_caller();
169 +
170 + $this->p->debug->log_args( array(
171 + 'term_id' => $term_id,
172 + 'tax_slug' => $tax_slug,
173 + ) );
174 + }
175 +
176 + static $local_fifo = array();
177 +
178 + /*
179 + * Maybe return the array from the local cache.
180 + *
181 + * Term IDs in older WordPress versions are not unique, so use the term ID and the taxonomy slug as a cache index.
182 + */
183 + if ( isset( $local_fifo[ $term_id ][ $tax_slug ] ) ) {
184 +
185 + if ( ! $this->md_cache_disabled ) {
186 +
187 + if ( $this->p->debug->enabled ) {
188 +
189 + $this->p->debug->log( 'exiting early: term id ' . $term_id . ' mod array from local cache' );
190 + }
191 +
192 + return $local_fifo[ $term_id ][ $tax_slug ];
193 +
194 + } else unset( $local_fifo[ $term_id ][ $tax_slug ] );
195 + }
196 +
197 + /*
198 + * Maybe limit the number of array elements.
199 + */
200 + $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX );
201 +
202 + $mod = self::get_mod_defaults();
203 +
204 + /*
205 + * Common elements.
206 + */
207 + $mod[ 'id' ] = is_numeric( $term_id ) ? (int) $term_id : 0; // Cast as integer.
208 + $mod[ 'name' ] = 'term';
209 + $mod[ 'name_transl' ] = _x( 'term', 'module name', 'wpsso' );
210 + $mod[ 'obj' ] =& $this;
211 +
212 + /*
213 + * WpssoTerm elements.
214 + */
215 + $mod[ 'is_term' ] = true;
216 + $mod[ 'is_archive' ] = true; // Required for WpssoUtil->get_url_paged().
217 +
218 + if ( $mod[ 'id' ] ) { // Just in case.
219 +
220 + $mod[ 'wp_obj' ] = SucomUtilWP::get_term_object( $mod[ 'id' ], (string) $tax_slug );
221 +
222 + if ( $mod[ 'wp_obj' ] instanceof WP_Term ) { // Just in case.
223 +
224 + $mod[ 'term_tax_id' ] = (int) $mod[ 'wp_obj' ]->term_taxonomy_id;
225 + $mod[ 'tax_slug' ] = (string) $mod[ 'wp_obj' ]->taxonomy;
226 +
227 + if ( $mod[ 'tax_slug' ] ) { // Just in case.
228 +
229 + $tax_obj = get_taxonomy( $mod[ 'tax_slug' ] );
230 +
231 + if ( $tax_obj instanceof WP_Taxonomy ) { // Just in case.
232 +
233 + if ( isset( $tax_obj->labels->name ) ) {
234 +
235 + $mod[ 'tax_label_plural' ] = $tax_obj->labels->name;
236 + }
237 +
238 + if ( isset( $tax_obj->labels->singular_name ) ) {
239 +
240 + $mod[ 'tax_label_single' ] = $tax_obj->labels->singular_name;
241 + }
242 +
243 + if ( isset( $tax_obj->public ) ) {
244 +
245 + $mod[ 'is_public' ] = $tax_obj->public ? true : false;
246 + }
247 + }
248 + }
249 +
250 + } else $mod[ 'wp_obj' ] = false;
251 + }
252 +
253 + /*
254 + * Filter the term mod array.
255 + */
256 + $mod = apply_filters( 'wpsso_get_term_mod', $mod, $term_id, $tax_slug );
257 +
258 + if ( $this->p->debug->enabled ) {
259 +
260 + $this->p->debug->log_arr( 'mod', $mod );
261 + }
262 +
263 + /*
264 + * Maybe save the array to the local cache.
265 + */
266 + if ( ! $this->md_cache_disabled ) {
267 +
268 + if ( ! isset( $local_fifo[ $term_id ] ) ) {
269 +
270 + $local_fifo[ $term_id ] = array();
271 + }
272 +
273 + $local_fifo[ $term_id ][ $tax_slug ] = $mod;
274 +
275 + if ( $this->p->debug->enabled ) {
276 +
277 + $this->p->debug->log_size( 'local_fifo', $local_fifo );
278 + }
279 + }
280 +
281 + return $mod;
282 + }
283 +
284 + public function get_mod_wp_object( array $mod ) {
285 +
286 + if ( $mod[ 'wp_obj' ] instanceof WP_Term ) {
287 +
288 + return $mod[ 'wp_obj' ];
289 + }
290 +
291 + return SucomUtilWP::get_term_object( $mod[ 'id' ], $mod[ 'tax_slug' ] );
292 + }
293 +
294 + /*
295 + * Option handling methods:
296 + *
297 + * get_defaults()
298 + * get_options()
299 + * save_options()
300 + * delete_options()
301 + */
302 + public function get_options( $term_id, $md_key = false, $filter_opts = true, $merge_defs = false ) {
303 +
304 + if ( $this->p->debug->enabled ) {
305 +
306 + $this->p->debug->mark_caller();
307 +
308 + $this->p->debug->log_args( array(
309 + 'term_id' => $term_id,
310 + 'md_key' => $md_key,
311 + 'filter_opts' => $filter_opts,
312 + 'merge_defs' => $merge_defs, // Fallback to value in meta defaults.
313 + ) );
314 + }
315 +
316 + static $local_fifo = array();
317 +
318 + /*
319 + * Use $term_id and $filter_opts to create the cache ID string, but do not add $merge_defs.
320 + */
321 + $cache_id = SucomUtil::get_assoc_salt( array( 'id' => $term_id, 'filter' => $filter_opts ) );
322 +
323 + /*
324 + * Maybe initialize a new local cache element. Use isset() instead of empty() to allow for an empty array.
325 + */
326 + if ( ! isset( $local_fifo[ $cache_id ] ) ) {
327 +
328 + /*
329 + * Maybe limit the number of array elements.
330 + */
331 + $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX );
332 +
333 + $local_fifo[ $cache_id ] = null; // Create an element to reference.
334 + }
335 +
336 + $md_opts =& $local_fifo[ $cache_id ]; // Reference the local cache element.
337 +
338 + if ( null === $md_opts ) { // Maybe read metadata into a new local cache element.
339 +
340 + if ( $this->p->debug->enabled ) {
341 +
342 + $this->p->debug->log( 'getting metadata for term id ' . $term_id );
343 + }
344 +
345 + $md_opts = self::get_meta( $term_id, WPSSO_META_NAME, true );
346 +
347 + if ( ! is_array( $md_opts ) ) {
348 +
349 + $md_opts = array(); // WPSSO_META_NAME not found.
350 + }
351 +
352 + unset( $md_opts[ 'opt_filtered' ] ); // Just in case.
353 +
354 + /*
355 + * Check if options need to be upgraded and saved.
356 + */
357 + if ( $this->p->opt->is_upgrade_required( $md_opts ) ) {
358 +
359 + $md_opts = $this->upgrade_options( $md_opts, $term_id );
360 +
361 + self::update_meta( $term_id, WPSSO_META_NAME, $md_opts );
362 + }
363 + }
364 +
365 + if ( $filter_opts ) {
366 +
367 + if ( ! empty( $md_opts[ 'opt_filtered' ] ) ) { // Set before calling filters to prevent recursion.
368 +
369 + if ( $this->p->debug->enabled ) {
370 +
371 + $this->p->debug->log( 'skipping filters: options already filtered' );
372 + }
373 +
374 + } else {
375 +
376 + if ( $this->p->debug->enabled ) {
377 +
378 + $this->p->debug->log( 'setting opt_filtered to 1' );
379 + }
380 +
381 + $md_opts[ 'opt_filtered' ] = 1; // Set before calling filters to prevent recursion.
382 +
383 + if ( $this->p->debug->enabled ) {
384 +
385 + $this->p->debug->log( 'required call to WpssoTerm->get_mod() for term ID ' . $term_id );
386 + }
387 +
388 + $mod = $this->get_mod( $term_id );
389 +
390 + /*
391 + * Overwrite parent options with those of the child, allowing only undefined child options
392 + * to be inherited from the parent.
393 + */
394 + if ( $this->p->debug->enabled ) {
395 +
396 + $this->p->debug->log( 'inheriting parent metadata options for term id ' . $term_id );
397 + }
398 +
399 + $parent_opts = $this->get_inherited_md_opts( $mod );
400 +
401 + if ( ! empty( $parent_opts ) ) {
402 +
403 + $md_opts = array_merge( $parent_opts, $md_opts );
404 + }
405 +
406 + $filter_name = 'wpsso_get_md_options';
407 +
408 + if ( $this->p->debug->enabled ) {
409 +
410 + $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
411 + }
412 +
413 + $md_opts = apply_filters( 'wpsso_get_md_options', $md_opts, $mod );
414 +
415 + $filter_name = 'wpsso_get_' . $mod[ 'name' ] . '_options';
416 +
417 + if ( $this->p->debug->enabled ) {
418 +
419 + $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
420 + }
421 +
422 + $md_opts = apply_filters( $filter_name, $md_opts, $term_id, $mod );
423 +
424 + $filter_name = 'wpsso_sanitize_md_options';
425 +
426 + if ( $this->p->debug->enabled ) {
427 +
428 + $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
429 + }
430 +
431 + $md_opts = apply_filters( $filter_name, $md_opts, $mod );
432 + }
433 + }
434 +
435 + /*
436 + * Maybe save the array to the local cache.
437 + */
438 + if ( $this->md_cache_disabled ) {
439 +
440 + $deref_md_opts = $local_fifo[ $cache_id ]; // Dereference.
441 +
442 + unset( $local_fifo, $md_opts );
443 +
444 + return $this->return_options( $term_id, $deref_md_opts, $md_key, $merge_defs );
445 + }
446 +
447 + return $this->return_options( $term_id, $md_opts, $md_key, $merge_defs );
448 + }
449 +
450 + /*
451 + * Use $term_tax_id = false to extend WpssoAbstractWpMeta->save_options().
452 + */
453 + public function save_options( $term_id, $term_tax_id = false ) {
454 +
455 + if ( $this->p->debug->enabled ) {
456 +
457 + $this->p->debug->log_args( array(
458 + 'term_id' => $term_id,
459 + 'term_tax_id' => $term_tax_id,
460 + ) );
461 + }
462 +
463 + if ( empty( $term_id ) ) { // Just in case.
464 +
465 + if ( $this->p->debug->enabled ) {
466 +
467 + $this->p->debug->log( 'exiting early: term id is empty' );
468 + }
469 +
470 + return;
471 +
472 + } elseif ( ! $this->verify_submit_nonce() ) {
473 +
474 + if ( $this->p->debug->enabled ) {
475 +
476 + $this->p->debug->log( 'exiting early: verify_submit_nonce failed' );
477 + }
478 +
479 + return;
480 +
481 + /*
482 + * Check user capability for the term id.
483 + */
484 + } elseif ( ! $this->user_can_edit( $term_id, $term_tax_id ) ) {
485 +
486 + if ( $this->p->debug->enabled ) {
487 +
488 + $user_id = get_current_user_id();
489 +
490 + $this->p->debug->log( 'exiting early: user id ' . $user_id . ' cannot edit term id ' . $term_id );
491 + }
492 +
493 + return;
494 + }
495 +
496 + $this->md_cache_disable(); // Disable the local cache.
497 +
498 + $term_obj = get_term_by( 'term_taxonomy_id', $term_tax_id, $tax_slug = '' );
499 +
500 + $mod = isset( $term_obj->taxonomy ) ? $this->get_mod( $term_id, $term_obj->taxonomy ) : $mod = $this->get_mod( $term_id );
501 +
502 + $md_opts = $this->get_submit_opts( $mod ); // Merge previous + submitted options and then sanitize.
503 +
504 + $this->md_cache_enable(); // Re-enable the local cache.
505 +
506 + if ( false === $md_opts ) {
507 +
508 + if ( $this->p->debug->enabled ) {
509 +
510 + $this->p->debug->log( 'exiting early: returned submit options is false' );
511 + }
512 +
513 + return;
514 + }
515 +
516 + $md_opts = apply_filters( 'wpsso_save_md_options', $md_opts, $mod );
517 +
518 + $md_opts = apply_filters( 'wpsso_save_' . $mod[ 'name' ] . '_options', $md_opts, $term_id, $mod );
519 +
520 + return self::update_meta( $term_id, WPSSO_META_NAME, $md_opts );
521 + }
522 +
523 + /*
524 + * Use $term_tax_id = false to extend WpssoAbstractWpMeta->delete_options().
525 + */
526 + public function delete_options( $term_id, $term_tax_id = false ) {
527 +
528 + return self::delete_meta( $term_id, WPSSO_META_NAME );
529 + }
530 +
531 + /*
532 + * Get all publicly accessible term ids.
533 + */
534 + public static function get_public_ids( array $terms_args = array() ) {
535 +
536 + $wpsso =& Wpsso::get_instance();
537 +
538 + if ( $wpsso->debug->enabled ) {
539 +
540 + $wpsso->debug->mark();
541 + }
542 +
543 + $mtime_start = microtime( $get_float = true );
544 +
545 + $tax_names = empty( $terms_args[ 'taxonomy' ] ) ? SucomUtilWP::get_taxonomies( $output = 'names' ) : array( $terms_args[ 'taxonomy' ] );
546 +
547 + $terms_args = array_merge( $terms_args, array( 'fields' => 'ids' ) );
548 +
549 + $public_ids = array();
550 +
551 + foreach ( $tax_names as $name ) {
552 +
553 + /*
554 + * See https://developer.wordpress.org/reference/classes/wp_term_query/__construct/.
555 + */
556 + $query_args = array_merge( $terms_args, array( 'taxonomy' => $name ) );
557 +
558 + $terms_ids = get_terms( $query_args );
559 +
560 + if ( is_array( $terms_ids ) ) {
561 +
562 + foreach ( $terms_ids as $term_id ) {
563 +
564 + if ( is_numeric( $term_id ) ) {
565 +
566 + $public_ids[ $term_id ] = $term_id; // Prevents duplicates.
567 + }
568 + }
569 + }
570 + }
571 +
572 + unset( $query_args, $terms_ids );
573 +
574 + /*
575 + * Sort public term IDs with the newest term ID first.
576 + *
577 + * Note that rsort() assigns new keys to elements in the array.
578 + *
579 + * See https://www.php.net/manual/en/function.rsort.php.
580 + */
581 + rsort( $public_ids );
582 +
583 + $mtime_total = microtime( $get_float = true ) - $mtime_start;
584 +
585 + if ( $wpsso->debug->enabled ) {
586 +
587 + $wpsso->debug->log( count( $public_ids ) . ' term IDs returned in ' . sprintf( '%0.3f secs', $mtime_total ) );
588 + }
589 +
590 + return apply_filters( 'wpsso_term_public_ids', $public_ids, $terms_args );
591 + }
592 +
593 + /*
594 + * Get the posts for a term.
595 + *
596 + * Returns an array of post ids for a given $mod object, including posts in child terms as well.
597 + *
598 + * The 'posts_per_page' value should be set for an archive page before calling this method.
599 + *
600 + * See WpssoAbstractWpMeta->get_posts_mods().
601 + */
602 + public function get_posts_ids( array $mod ) {
603 +
604 + if ( $this->p->debug->enabled ) {
605 +
606 + $this->p->debug->mark();
607 + }
608 +
609 + if ( empty( $mod[ 'is_term' ] ) ) {
610 +
611 + if ( $this->p->debug->enabled ) {
612 +
613 + $this->p->debug->log( 'exiting early: ' . $mod[ 'name' ] . ' ID ' . $mod[ 'id' ] . ' is not a term' );
614 + }
615 +
616 + return array();
617 + }
618 +
619 + $posts_args = array_merge( array(
620 + 'has_password' => false,
621 + 'order' => 'DESC', // Newest first.
622 + 'orderby' => 'date',
623 + 'post_status' => 'publish', // Only 'publish' (not 'auto-draft', 'draft', 'future', 'inherit', 'pending', 'private', or 'trash').
624 + 'post_type' => 'any', // Return posts, pages, or any custom post type.
625 + 'tax_query' => array(
626 + array(
627 + 'taxonomy' => $mod[ 'tax_slug' ],
628 + 'field' => 'term_id',
629 + 'terms' => $mod[ 'id' ],
630 + 'include_children' => true
631 + )
632 + ),
633 + ), $mod[ 'posts_args' ], array( 'fields' => 'ids' ) ); // Return an array of post ids.
634 +
635 + if ( $this->p->debug->enabled ) {
636 +
637 + $this->p->debug->log( 'getting posts for ' . $mod[ 'name' ] . ' ID ' . $mod[ 'id' ] . ' in taxonomy ' . $mod[ 'tax_slug' ] );
638 +
639 + $this->p->debug->log_arr( 'posts_args', $posts_args );
640 + }
641 +
642 + $mtime_start = microtime( $get_float = true );
643 +
644 + $posts_ids = SucomUtilWP::get_posts( $posts_args ); // Alternative to get_posts() that does not exclude sticky posts.
645 +
646 + $mtime_total = microtime( $get_float = true ) - $mtime_start;
647 +
648 + if ( $this->p->debug->enabled ) {
649 +
650 + $this->p->debug->log( count( $posts_ids ) . ' post IDs returned in ' . sprintf( '%0.3f secs', $mtime_total ) );
651 + }
652 +
653 + return $posts_ids;
654 + }
655 +
656 + public function add_term_column_headings( $columns ) {
657 +
658 + if ( $this->p->debug->enabled ) {
659 +
660 + $this->p->debug->mark();
661 + }
662 +
663 + return $this->add_column_headings( $columns, $opt_suffix = 'tax_' . $this->query_tax_slug );
664 + }
665 +
666 + public function get_update_meta_cache( $term_id ) {
667 +
668 + return SucomUtilWP::get_update_meta_cache( $term_id, $meta_type = 'term' );
669 + }
670 +
671 + /*
672 + * Hooked into the current_screen action.
673 + *
674 + * Sets the parent::$head_tags and parent::$head_info class properties.
675 + */
676 + public function load_meta_page( $screen = false ) {
677 +
678 + if ( $this->p->debug->enabled ) {
679 +
680 + $this->p->debug->mark();
681 + }
682 +
683 + /*
684 + * All meta modules set this property, so use it to optimize code execution.
685 + */
686 + if ( false !== parent::$head_tags || ! isset( $screen->id ) ) {
687 +
688 + return;
689 + }
690 +
691 + if ( $this->p->debug->enabled ) {
692 +
693 + $this->p->debug->log( 'screen id = ' . $screen->id );
694 + $this->p->debug->log( 'query tax slug = ' . $this->query_tax_slug );
695 + }
696 +
697 + switch ( $screen->id ) {
698 +
699 + case 'edit-' . $this->query_tax_slug:
700 +
701 + $mod = $this->get_mod( $this->query_term_id, $this->query_tax_slug );
702 +
703 + break;
704 +
705 + default:
706 +
707 + if ( $this->p->debug->enabled ) {
708 +
709 + $this->p->debug->log( 'exiting early: not a recognized term page' );
710 + }
711 +
712 + return;
713 + }
714 +
715 + /*
716 + * Define parent::$head_tags and signal to other 'current_screen' actions that this is a term page.
717 + */
718 + parent::$head_tags = array(); // Used by WpssoAbstractWpMeta->is_meta_page().
719 +
720 + if ( $this->p->debug->enabled ) {
721 +
722 + $this->p->debug->log( 'term id = ' . $this->query_term_id );
723 + $this->p->debug->log( 'home url = ' . get_option( 'home' ) );
724 + $this->p->debug->log( 'locale current = ' . SucomUtilWP::get_locale() );
725 + $this->p->debug->log( 'locale default = ' . SucomUtilWP::get_locale( 'default' ) );
726 + $this->p->debug->log( 'locale mod = ' . SucomUtilWP::get_locale( $mod ) );
727 + $this->p->debug->log( SucomUtil::get_array_pretty( $mod ) );
728 + }
729 +
730 + if ( $this->query_term_id && ! empty( $this->p->options[ 'plugin_add_to_tax_' . $this->query_tax_slug ] ) ) {
731 +
732 + do_action( 'wpsso_admin_term_head', $mod, $screen->id );
733 +
734 + list(
735 + parent::$head_tags, // Used by WpssoAbstractWpMeta->is_meta_page().
736 + parent::$head_info // Used by WpssoAbstractWpMeta->check_head_info().
737 + ) = $this->p->util->cache->refresh_mod_head_meta( $mod );
738 +
739 + /*
740 + * Check for missing open graph image and description values.
741 + */
742 + if ( $mod[ 'id' ] && $mod[ 'is_public' ] ) { // Since WPSSO Core v7.0.0.
743 +
744 + $this->check_head_info( $mod );
745 + }
746 + }
747 +
748 + $action_query = 'wpsso-action';
749 +
750 + if ( ! empty( $_GET[ $action_query ] ) ) {
751 +
752 + $action_name = SucomUtil::sanitize_hookname( $_GET[ $action_query ] );
753 +
754 + if ( $this->p->debug->enabled ) {
755 +
756 + $this->p->debug->log( 'found action query: ' . $action_name );
757 + }
758 +
759 + if ( empty( $_GET[ WPSSO_NONCE_NAME ] ) ) { // WPSSO_NONCE_NAME is an md5() string
760 +
761 + if ( $this->p->debug->enabled ) {
762 +
763 + $this->p->debug->log( 'nonce token query field missing' );
764 + }
765 +
766 + } elseif ( ! wp_verify_nonce( $_GET[ WPSSO_NONCE_NAME ], WpssoAdmin::get_nonce_action() ) ) {
767 +
768 + $this->p->notice->err( sprintf( __( 'Nonce token validation failed for %1$s action "%2$s".', 'wpsso' ), 'term', $action_name ) );
769 +
770 + } else {
771 +
772 + $_SERVER[ 'REQUEST_URI' ] = remove_query_arg( array( $action_query, WPSSO_NONCE_NAME ) );
773 +
774 + switch ( $action_name ) {
775 +
776 + default:
777 +
778 + do_action( 'wpsso_load_meta_page_term_' . $action_name, $this->query_term_id );
779 +
780 + break;
781 + }
782 + }
783 + }
784 + }
785 +
786 + /*
787 + * Use $tax_slug = false to extend WpssoAbstractWpMeta->add_meta_boxes().
788 + */
789 + public function add_meta_boxes( $term_obj, $tax_slug = false ) {
790 +
791 + if ( $this->p->debug->enabled ) {
792 +
793 + $this->p->debug->mark();
794 + }
795 +
796 + $term_id = empty( $term_obj->term_id ) ? 0 : $term_obj->term_id;
797 + $tax_obj = get_taxonomy( $tax_slug );
798 +
799 + if ( empty( $this->p->options[ 'plugin_add_to_tax_' . $tax_slug ] ) ) {
800 +
801 + if ( $this->p->debug->enabled ) {
802 +
803 + $this->p->debug->log( 'exiting early: cannot add metabox to taxonomy "' . $tax_slug . '"' );
804 + }
805 +
806 + return;
807 + }
808 +
809 + $capability = $tax_obj->cap->edit_terms;
810 +
811 + if ( ! current_user_can( $capability, $term_id ) ) {
812 +
813 + if ( $this->p->debug->enabled ) {
814 +
815 + $this->p->debug->log( 'exiting early: cannot ' . $capability . ' for term id ' . $term_id );
816 + }
817 +
818 + return;
819 + }
820 +
821 + $metabox_id = $this->p->cf[ 'meta' ][ 'id' ];
822 + $metabox_title = _x( $this->p->cf[ 'meta' ][ 'title' ], 'metabox title', 'wpsso' );
823 + $metabox_screen = 'wpsso-term';
824 + $metabox_context = 'normal';
825 + $metabox_prio = 'default';
826 + $callback_args = array( // Second argument passed to the callback.
827 + 'metabox_id' => $metabox_id,
828 + 'metabox_title' => $metabox_title,
829 + '__block_editor_compatible_meta_box' => true,
830 + );
831 +
832 + if ( $this->p->debug->enabled ) {
833 +
834 + $this->p->debug->log( 'adding metabox id wpsso_' . $metabox_id . ' for screen ' . $metabox_screen );
835 + }
836 +
837 + add_meta_box( 'wpsso_' . $metabox_id, $metabox_title, array( $this, 'show_metabox_' . $metabox_id ),
838 + $metabox_screen, $metabox_context, $metabox_prio, $callback_args );
839 + }
840 +
841 + public function show_metaboxes( $term_obj, $tax_slug ) {
842 +
843 + $term_id = empty( $term_obj->term_id ) ? 0 : $term_obj->term_id;
844 + $tax_obj = get_taxonomy( $tax_slug );
845 + $capability = $tax_obj->cap->edit_terms;
846 +
847 + if ( empty( $this->p->options[ 'plugin_add_to_tax_' . $tax_slug ] ) ) {
848 +
849 + return;
850 +
851 + } elseif ( ! current_user_can( $capability, $term_id ) ) {
852 +
853 + return;
854 + }
855 +
856 + $metabox_screen = 'wpsso-term';
857 + $metabox_context = 'normal';
858 +
859 + /*
860 + * Keep the original 800px width for the form table and allow metaboxes to take the screen width.
861 + */
862 + echo '<style type="text/css">';
863 + echo 'div.wrap form#edittag { max-width:none; }';
864 + echo 'div.wrap form#edittag table.form-table { max-width:800px; }';
865 + echo '</style>' . "\n";
866 + echo '<div class="metabox-holder">' . "\n";
867 +
868 + do_meta_boxes( $metabox_screen, $metabox_context, $term_obj );
869 +
870 + echo "\n" . '</div><!-- .metabox-holder -->' . "\n";
871 + }
872 +
873 + public function ajax_get_metabox_sso() {
874 +
875 + die( -1 ); // Nothing to do.
876 + }
877 +
878 + /*
879 + * Hooked to these actions:
880 + *
881 + * do_action( "created_$taxonomy", $term_id, $tt_id );
882 + * do_action( "edited_$taxonomy", $term_id, $tt_id );
883 + * do_action( "delete_$taxonomy", $term_id, $tt_id, $deleted_term );
884 + *
885 + * Also called by WpssoPost::clear_cache() to clear the post term cache.
886 + *
887 + * Use $term_tax_id = false to extend WpssoAbstractWpMeta->clear_cache().
888 + */
889 + public function clear_cache( $term_id, $term_tax_id = false ) {
890 +
891 + if ( $this->p->debug->enabled ) {
892 +
893 + $this->p->debug->log_args( array(
894 + 'term_id' => $term_id,
895 + 'term_tax_id' => $term_tax_id,
896 + ) );
897 + }
898 +
899 + static $do_once = array(); // Just in case - prevent recursion.
900 +
901 + if ( isset( $do_once[ $term_id ][ $term_tax_id ] ) ) return; // Stop here.
902 +
903 + $do_once[ $term_id ][ $term_tax_id ] = true;
904 +
905 + if ( empty( $term_id ) ) return; // Just in case.
906 +
907 + $term_obj = get_term_by( 'term_taxonomy_id', $term_tax_id, $tax_slug = '' );
908 +
909 + $mod = isset( $term_obj->taxonomy ) ? $this->get_mod( $term_id, $term_obj->taxonomy ) : $this->get_mod( $term_id );
910 +
911 + unset( $term_obj );
912 +
913 + $this->clear_mod_cache( $mod );
914 +
915 + do_action( 'wpsso_clear_term_cache', $term_id, $mod );
916 + }
917 +
918 + /*
919 + * Refresh the cache for a single term ID.
920 + *
921 + * Use $term_tax_id = false to extend WpssoAbstractWpMeta->refresh_cache().
922 + */
923 + public function refresh_cache( $term_id, $term_tax_id = false ) {
924 +
925 + static $do_once = array(); // Just in case - prevent recursion.
926 +
927 + if ( isset( $do_once[ $term_id ][ $term_tax_id ] ) ) return; // Stop here.
928 +
929 + $do_once[ $term_id ][ $term_tax_id ] = true;
930 +
931 + if ( empty( $term_id ) ) return; // Just in case.
932 +
933 + $term_obj = get_term_by( 'term_taxonomy_id', $term_tax_id, $tax_slug = '' );
934 +
935 + $mod = isset( $term_obj->taxonomy ) ? $this->get_mod( $term_id, $term_obj->taxonomy ) : $this->get_mod( $term_id );
936 +
937 + unset( $term_obj );
938 +
939 + $this->p->util->cache->refresh_mod_head_meta( $mod );
940 +
941 + do_action( 'wpsso_refresh_term_cache', $term_id, $mod );
942 + }
943 +
944 + /*
945 + * Check user capability for the term id.
946 + *
947 + * Use $term_tax_id = false to extend WpssoAbstractWpMeta->user_can_edit().
948 + */
949 + public function user_can_edit( $term_id, $term_tax_id = false ) {
950 +
951 + $term_obj = get_term_by( 'term_taxonomy_id', $term_tax_id, $tax_slug = '' );
952 + $tax_obj = get_taxonomy( $term_obj->taxonomy );
953 + $capability = $tax_obj->cap->edit_terms;
954 +
955 + if ( ! current_user_can( $capability, $term_id ) ) {
956 +
957 + if ( $this->p->debug->enabled ) {
958 +
959 + $this->p->debug->log( 'exiting early: cannot ' . $capability . ' for term id ' . $term_id );
960 + }
961 +
962 + /*
963 + * Add notice only if the admin notices have not already been shown.
964 + */
965 + if ( $this->p->notice->is_admin_pre_notices() ) {
966 +
967 + $this->p->notice->err( sprintf( __( 'Insufficient privileges to edit term ID %1$s.', 'wpsso' ), $term_id ) );
968 + }
969 +
970 + return false;
971 + }
972 +
973 + return true;
974 + }
975 +
976 + /*
977 + * If $meta_key is en empty string, retrieves all metadata for the specified object ID.
978 + *
979 + * See https://developer.wordpress.org/reference/functions/get_metadata/.
980 + */
981 + public static function get_meta( $term_id, $meta_key = '', $single = false ) {
982 +
983 + return get_metadata( 'term', $term_id, $meta_key, $single );
984 + }
985 +
986 + public static function update_meta( $term_id, $meta_key, $value ) {
987 +
988 + return update_metadata( 'term', $term_id, $meta_key, $value );
989 + }
990 +
991 + public static function delete_meta( $term_id, $meta_key ) {
992 +
993 + return delete_metadata( 'term', $term_id, $meta_key );
994 + }
995 + }
996 +}