PluginProbe
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms / 3.40.0
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms v3.40.0
3.54.0 3.53.0 3.52.0 3.51.0 3.50.0 3.45.0 3.38.0 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.40.0 3.40.1 3.41.0 3.42.0 3.43.0 3.44.0 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 All 178 releases
simple-tags / inc / class.client.tagcloud.php

class.client.tagcloud.php in Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms 3.40.0, at inc/class.client.tagcloud.php

1,238 lines 37.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class SimpleTags_Client_TagCloud {
4 /**
5 * Init Embedded tag cloud
6 *
7 * SimpleTags_Client_TagCloud constructor.
8 */
9 public function __construct() {
10
11 }
12
13 /**
14 * Sort an array without accent for naturel order :)
15 *
16 * @param string $a
17 * @param string $b
18 *
19 * @return boolean
20 */
21 public static function uksort_by_name( $a = '', $b = '' ) {
22 return strnatcasecmp( remove_accents( $a ), remove_accents( $b ) );
23 }
24
25 /**
26 * Generate extended tag cloud
27 *
28 * @param string $args
29 *
30 * @return string|array
31 */
32 public static function extendedTagCloud( $args = '', $copyright = true ) {
33 $defaults = array(
34 // Simple Tag global options defaults
35 'selectionby' => 'count',
36 'selection' => 'desc',
37 'orderby' => 'random',
38 'order' => 'asc',
39 'format' => 'border',
40 'xformat' => __( '<a href="%tag_link%" id="tag-link-%tag_id%" class="st-tags t%tag_scale%" title="%tag_count% topics" %tag_rel% style="%tag_size% %tag_color%">%tag_name%</a>', 'simple-tags' ),
41 'number' => 20,
42 'notagstext' => __( 'No tags.', 'simple-tags' ),
43 'title' => __( '<h4>Tag Cloud</h4>', 'simple-tags' ),
44 'maxcolor' => '#000000',
45 'mincolor' => '#353535',
46 'largest' => 12,
47 'smallest' => 12,
48 'unit' => 'pt',
49 'taxonomy' => 'post_tag', // Note: saved as an option but no UI to set it
50 'parent_term' => '',
51 'display_mode' => 'parents_and_sub',
52 // Simple Tag other defaults
53 'size' => 'true',
54 'color' => 'true',
55 'exclude' => '',
56 'exclude_terms' => '',
57 'include' => '',
58 'limit_days' => 0,
59 'min_usage' => 0,
60 'category' => 0,
61 'ID' => 0,
62 'hide_title' => 0,
63 'hide_output' => 0,
64 'post_type' => '',
65 'wrap_class' => '',
66 'link_class' => '',
67 'before' => '',
68 'after' => '',
69 'hide_terms' => 0,
70 );
71
72 // Get options
73 $options = SimpleTags_Plugin::get_option();
74 $enable_hidden_terms = SimpleTags_Plugin::get_option_value('enable_hidden_terms');
75
76 // Get values in DB
77 $defaults['selectionby'] = $options['cloud_selectionby'];
78 $defaults['selection'] = $options['cloud_selection'];
79 $defaults['orderby'] = $options['cloud_orderby'];
80 $defaults['order'] = $options['cloud_order'];
81 $defaults['format'] = $options['cloud_format'];
82 $defaults['xformat'] = $options['cloud_xformat'];
83 $defaults['number'] = $options['cloud_limit_qty'];
84 $defaults['notagstext'] = $options['cloud_notagstext'];
85 $defaults['title'] = $options['cloud_title'];
86 $defaults['maxcolor'] = $options['cloud_max_color'];
87 $defaults['mincolor'] = $options['cloud_min_color'];
88 $defaults['largest'] = $options['cloud_max_size'];
89 $defaults['smallest'] = $options['cloud_min_size'];
90 $defaults['unit'] = $options['cloud_unit'];
91 $defaults['taxonomy'] = $options['cloud_taxonomy'];
92 $defaults['parent_term'] = $options['cloud_parent_term'];
93 $defaults['display_mode'] = $options['cloud_display_mode'];
94
95 $adv_usage = $options['cloud_adv_usage'];
96 if ( empty( $args ) ) {
97 $args = $adv_usage;
98 } else {
99 $args = $adv_usage . "&" . $args;
100 }
101 $args = wp_parse_args( $args, $defaults );
102
103 // Add compatibility tips with old field syntax
104 if ( isset( $args['cloud_sort'] ) ) {
105 $args['cloud_order'] = $args['cloud_sort'];
106 unset( $args['cloud_sort'] );
107 }
108
109 // Translate selection order
110 if ( isset( $args['cloud_order'] ) ) {
111 $args['orderby'] = self::compatOldOrder( $args['cloud_order'], 'orderby' );
112 $args['order'] = self::compatOldOrder( $args['cloud_order'], 'order' );
113 }
114
115 // Category names to ID codes
116 if ( isset( $args['category'] ) ) {
117 $category = explode( ",", $args['category'] );
118 foreach ( $category as $key => $name ) {
119 $category[ $key ] = is_numeric( $name ) ? $name : get_cat_ID( $name );
120 if ( $category[ $key ] == 0 ) {
121 unset( $category[ $key ] );
122 }
123 }
124 $args['category'] = implode( ",", $category );
125 }
126
127 // Get correct taxonomy ?
128 $taxonomy = self::_get_current_taxonomy( $args['taxonomy'] );
129
130 // Get terms
131 $terms = self::getTags( $args, $taxonomy );
132
133 // Remove hidden terms if enabled
134 if ($enable_hidden_terms && !empty($args['hide_terms'])) {
135 $hidden_terms = get_transient('taxopress_hidden_terms_' . $args['taxonomy']);
136 if (!empty($hidden_terms) && is_array($hidden_terms)) {
137 $terms = array_filter($terms, function ($term) use ($hidden_terms) {
138 return !in_array($term->term_id, $hidden_terms);
139 });
140 }
141 }
142
143 extract( $args ); // Params to variables
144
145 // Process exclude_terms
146 if ( ! empty( $exclude_terms ) ) {
147 // Convert the exclude terms string into a set (associative array) for faster lookup
148 $exclude_terms_set = array_flip(array_map('strtolower', array_map('trim', explode(',', $exclude_terms))));
149 } else {
150 $exclude_terms_set = array();
151 }
152
153 // Filter out excluded terms
154 if ( ! empty( $exclude_terms_set ) ) {
155 // Use array_filter to remove terms that exist in the exclude set
156 $terms = array_filter( $terms, function( $term ) use ( $exclude_terms_set ) {
157 return ! isset( $exclude_terms_set[ strtolower($term->name) ] );
158 });
159 }
160
161 // If empty use default xformat !
162 if ( empty( $xformat ) ) {
163 $xformat = $defaults['xformat'];
164 }
165
166 $xformat = taxopress_sanitize_text_field($xformat);
167
168 //remove title if in settings
169 if((int)$hide_title > 0){
170 $title = '';
171 }
172
173 if ( empty( $terms ) ) {
174 if((int)$hide_output === 0){
175 return SimpleTags_Client::output_content( 'st-tag-cloud', $format, $title, $notagstext, $copyright, '', $wrap_class, $link_class );
176 }else{
177 return '';
178 }
179 }
180
181 $counts = $terms_data = array();
182 foreach ( (array) $terms as $term ) {
183 $counts[ $term->name ] = $term->count;
184 $terms_data[ $term->name ] = $term;
185 }
186
187 // Remove temp data from memory
188 $terms = array();
189 unset( $terms );
190
191 // Use full RBG code
192 if ( strlen( $maxcolor ) == 4 ) {
193 $maxcolor = $maxcolor . substr( $maxcolor, 1, strlen( $maxcolor ) );
194 }
195 if ( strlen( $mincolor ) == 4 ) {
196 $mincolor = $mincolor . substr( $mincolor, 1, strlen( $mincolor ) );
197 }
198
199 // Check as smallest inferior or egal to largest
200 if ( $smallest > $largest ) {
201 $smallest = $largest;
202 }
203
204 // Scaling - Hard value for the moment
205 $scale_min = 0;
206 $scale_max = 10;
207
208 $minval = min( $counts );
209 $maxval = max( $counts );
210
211 $minout = max( $scale_min, 0 );
212 $maxout = max( $scale_max, $minout );
213
214 $scale = ( $maxval > $minval ) ? ( ( $maxout - $minout ) / ( $maxval - $minval ) ) : 0;
215
216 // HTML Rel (tag/no-follow)
217 $rel = SimpleTags_Client::get_rel_attribut();
218
219 // Remove color marquer if color = false
220 if ( $color == 'false' ) {
221 $xformat = str_replace( '%tag_color%', '', $xformat );
222 }
223
224 // Remove size marquer if size = false
225 if ( $size == 'false' ) {
226 $xformat = str_replace( '%tag_size%', '', $xformat );
227 }
228
229
230 // Order terms before output
231 // count, name, rand | asc, desc
232 $orderby = strtolower($orderby);
233 $order = strtolower($order);
234
235 if (in_array($format, ['parent/child']) && $args['display_mode'] === 'parents_and_sub') {
236 $terms = self::getTags($args, $taxonomy);
237
238 // Check if terms is empty or invalid
239 if (empty($terms)) {
240 return SimpleTags_Client::output_content('st-tag-cloud', $format, $title, $notagstext, $copyright, '', $wrap_class, $link_class);
241 }
242
243 // First, group terms by parent term
244 $grouped_terms = [];
245 foreach ($terms as $term) {
246 // Only group terms with a parent (excluding standalone terms)
247 if ($term->parent != 0) {
248 $parent_id = $term->parent;
249 if (!isset($grouped_terms[$parent_id])) {
250 $grouped_terms[$parent_id] = [];
251 }
252 $grouped_terms[$parent_id][] = $term;
253 }
254 }
255
256 // Then, sort each group by name
257 foreach ($grouped_terms as &$parent_group) {
258 usort($parent_group, function ($a, $b) {
259 return strcmp($a->name, $b->name);
260 });
261 }
262
263 // Merge all grouped terms into a single array, ensuring correct parent-child order
264 $terms = [];
265 foreach ($grouped_terms as $parent_group) {
266 $terms = array_merge($terms, $parent_group);
267 }
268 }
269
270 // Custom drag-and-drop order: always check first!
271 if ($orderby === 'taxopress_term_order') {
272 $custom_order = get_option('taxopress_term_order_' . $taxonomy, []);
273 if (!empty($custom_order)) {
274 $terms_by_id = [];
275 foreach ($terms_data as $term_name => $term_obj) {
276 if (is_object($term_obj) && isset($term_obj->term_id)) {
277 $terms_by_id[$term_obj->term_id] = $term_obj;
278 }
279 }
280 $ordered_terms = [];
281 foreach ($custom_order as $term_id) {
282 if (isset($terms_by_id[$term_id])) {
283 $ordered_terms[$terms_by_id[$term_id]->name] = $terms_by_id[$term_id]->count;
284 unset($terms_by_id[$term_id]);
285 }
286 }
287 // Add any terms not in custom order at the end
288 foreach ($terms_by_id as $term_obj) {
289 $ordered_terms[$term_obj->name] = $term_obj->count;
290 }
291 if ($order === 'desc') {
292 $ordered_terms = array_reverse($ordered_terms, true);
293 }
294 $counts = $ordered_terms;
295 }
296 } else {
297 // Default sorting logic
298 if ($orderby == 'count') {
299 asort($counts);
300 } elseif ($orderby == 'name') {
301 uksort($counts, [__CLASS__, 'uksort_by_name']);
302 } else { // rand
303 SimpleTags_Client::random_array($counts);
304 }
305 $order = strtolower($order);
306 if ($order == 'desc' && $orderby != 'random') {
307 $counts = array_reverse($counts, true);
308 }
309 }
310
311 $output = array();
312
313 //update xformat with class link class
314 if(!empty(trim($link_class))){
315 $link_class = taxopress_format_class($link_class);
316 $xformat = taxopress_add_class_to_format($xformat, $link_class);
317 }
318
319 foreach ( (array) $counts as $term_name => $count ) {
320 if ( ! is_object( $terms_data[ $term_name ] ) ) {
321 continue;
322 }
323
324 $term = $terms_data[ $term_name ];
325 $scale_result = (int) ( $scale <> 0 ? ( ( $term->count - $minval ) * $scale + $minout ) : ( $scale_max - $scale_min ) / 2 );
326 //$output[] = SimpleTags_Client::format_internal_tag( $xformat, $term, $rel, $scale_result, $scale_max, $scale_min, $largest, $smallest, $unit, $maxcolor, $mincolor );
327 $formatted = SimpleTags_Client::format_internal_tag( $xformat, $term, $rel, $scale_result, $scale_max, $scale_min, $largest, $smallest, $unit, $maxcolor, $mincolor );
328 if ($format === 'table') {
329 $output[] = [
330 'html' => $formatted,
331 'count' => $term->count,
332 'term_name' => $term->name,
333 ];
334 } else {
335 $output[] = $formatted;
336 }
337 }
338
339 return SimpleTags_Client::output_content( 'st-tag-cloud', $format, $title, $output, $copyright, '', $wrap_class, $link_class, $before, $after, $taxonomy );
340 }
341
342
343 /**
344 * Generate extended tag result
345 *
346 * @param string $args
347 *
348 * @return array
349 */
350 public static function extendedTagResult( $args = '', $copyright = true ) {
351 $defaults = array(
352 // Simple Tag global options defaults
353 'selectionby' => 'count',
354 'selection' => 'desc',
355 'orderby' => 'random',
356 'order' => 'asc',
357 'format' => 'border',
358 'xformat' => __( '<a href="%tag_link%" id="tag-link-%tag_id%" class="st-tags t%tag_scale%" title="%tag_count% topics" %tag_rel% style="%tag_size% %tag_color%">%tag_name%</a>', 'simple-tags' ),
359 'number' => 20,
360 'notagstext' => __( 'No tags.', 'simple-tags' ),
361 'title' => __( '<h4>Tag Cloud</h4>', 'simple-tags' ),
362 'maxcolor' => '#000000',
363 'mincolor' => '#353535',
364 'largest' => 12,
365 'smallest' => 12,
366 'unit' => 'pt',
367 'taxonomy' => 'post_tag', // Note: saved as an option but no UI to set it
368 'parent_term' => '',
369 'display_mode' => 'parents_and_sub',
370 // Simple Tag other defaults
371 'size' => 'true',
372 'color' => 'true',
373 'exclude' => '',
374 'include' => '',
375 'limit_days' => 0,
376 'min_usage' => 0,
377 'category' => 0,
378 'hide_terms' => 0,
379 );
380
381 // Get options
382 $options = SimpleTags_Plugin::get_option();
383 $enable_hidden_terms = SimpleTags_Plugin::get_option_value('enable_hidden_terms');
384
385 // Get values in DB
386 $defaults['selectionby'] = $options['cloud_selectionby'];
387 $defaults['selection'] = $options['cloud_selection'];
388 $defaults['orderby'] = $options['cloud_orderby'];
389 $defaults['order'] = $options['cloud_order'];
390 $defaults['format'] = $options['cloud_format'];
391 $defaults['xformat'] = $options['cloud_xformat'];
392 $defaults['number'] = $options['cloud_limit_qty'];
393 $defaults['notagstext'] = $options['cloud_notagstext'];
394 $defaults['title'] = $options['cloud_title'];
395 $defaults['maxcolor'] = $options['cloud_max_color'];
396 $defaults['mincolor'] = $options['cloud_min_color'];
397 $defaults['largest'] = $options['cloud_max_size'];
398 $defaults['smallest'] = $options['cloud_min_size'];
399 $defaults['unit'] = $options['cloud_unit'];
400 $defaults['taxonomy'] = $options['cloud_taxonomy'];
401 $defaults['parent_term'] = $options['cloud_parent_term'];
402 $defaults['display_mode'] = $options['cloud_display_mode'];
403
404 $adv_usage = $options['cloud_adv_usage'];
405 if ( empty( $args ) ) {
406 $args = $adv_usage;
407 } else {
408 $args = $adv_usage . "&" . $args;
409 }
410 $args = wp_parse_args( $args, $defaults );
411
412 // Add compatibility tips with old field syntax
413 if ( isset( $args['cloud_sort'] ) ) {
414 $args['cloud_order'] = $args['cloud_sort'];
415 unset( $args['cloud_sort'] );
416 }
417
418 // Translate selection order
419 if ( isset( $args['cloud_order'] ) ) {
420 $args['orderby'] = self::compatOldOrder( $args['cloud_order'], 'orderby' );
421 $args['order'] = self::compatOldOrder( $args['cloud_order'], 'order' );
422 }
423
424 // Category names to ID codes
425 if ( isset( $args['category'] ) ) {
426 $category = explode( ",", $args['category'] );
427 foreach ( $category as $key => $name ) {
428 $category[ $key ] = is_numeric( $name ) ? $name : get_cat_ID( $name );
429 if ( $category[ $key ] == 0 ) {
430 unset( $category[ $key ] );
431 }
432 }
433 $args['category'] = implode( ",", $category );
434 }
435
436 // Get correct taxonomy ?
437 $taxonomy = self::_get_current_taxonomy( $args['taxonomy'] );
438
439 // Get terms
440 $terms = self::getTags( $args, $taxonomy );
441
442 // Remove hidden terms if enabled
443 if ($enable_hidden_terms && !empty($args['hide_terms'])) {
444 $hidden_terms = get_transient('taxopress_hidden_terms_' . $args['taxonomy']);
445 if (!empty($hidden_terms) && is_array($hidden_terms)) {
446 $terms = array_filter($terms, function ($term) use ($hidden_terms) {
447 return !in_array($term->term_id, $hidden_terms);
448 });
449 }
450 }
451 extract( $args ); // Params to variables
452
453 // If empty use default xformat !
454 if ( empty( $xformat ) ) {
455 $xformat = $defaults['xformat'];
456 }
457
458 if ( empty( $terms ) ) {
459 return [];
460 }
461
462 $counts = $terms_data = array();
463 foreach ( (array) $terms as $term ) {
464 $counts[ $term->name ] = $term->count;
465 $terms_data[ $term->name ] = $term;
466 }
467
468 // Remove temp data from memory
469 $terms = array();
470 unset( $terms );
471
472 // Use full RBG code
473 if ( strlen( $maxcolor ) == 4 ) {
474 $maxcolor = $maxcolor . substr( $maxcolor, 1, strlen( $maxcolor ) );
475 }
476 if ( strlen( $mincolor ) == 4 ) {
477 $mincolor = $mincolor . substr( $mincolor, 1, strlen( $mincolor ) );
478 }
479
480 // Check as smallest inferior or egal to largest
481 if ( $smallest > $largest ) {
482 $smallest = $largest;
483 }
484
485 // Scaling - Hard value for the moment
486 $scale_min = 0;
487 $scale_max = 10;
488
489 $minval = min( $counts );
490 $maxval = max( $counts );
491
492 $minout = max( $scale_min, 0 );
493 $maxout = max( $scale_max, $minout );
494
495 $scale = ( $maxval > $minval ) ? ( ( $maxout - $minout ) / ( $maxval - $minval ) ) : 0;
496
497 // HTML Rel (tag/no-follow)
498 $rel = SimpleTags_Client::get_rel_attribut();
499
500 // Remove color marquer if color = false
501 if ( $color == 'false' ) {
502 $xformat = str_replace( '%tag_color%', '', $xformat );
503 }
504
505 // Remove size marquer if size = false
506 if ( $size == 'false' ) {
507 $xformat = str_replace( '%tag_size%', '', $xformat );
508 }
509
510 // Order terms before output
511 // count, name, rand | asc, desc
512
513 $orderby = strtolower( $orderby );
514 if ( $orderby == 'count' ) {
515 asort( $counts );
516 } elseif ( $orderby == 'name' ) {
517 uksort( $counts, array( __CLASS__, 'uksort_by_name' ) );
518 } else { // rand
519 SimpleTags_Client::random_array( $counts );
520 }
521
522 $order = strtolower( $order );
523 if ( $order == 'desc' && $orderby != 'random' ) {
524 $counts = array_reverse( $counts );
525 }
526
527 $output = array();
528 foreach ( (array) $counts as $term_name => $count ) {
529 if ( ! is_object( $terms_data[ $term_name ] ) ) {
530 continue;
531 }
532 $output[] = $terms_data[ $term_name ];
533 }
534
535
536 return $output;
537 }
538
539 /**
540 * Check if taxonomy exist and return it, otherwise return default post tags.
541 *
542 * @param string|array $taxonomies
543 * @param boolean $force_single
544 *
545 * @return array|string
546 * @author WebFactory Ltd
547 */
548 public static function _get_current_taxonomy( $taxonomies, $force_single = false ) {
549 if ( is_array( $taxonomies ) ) {
550 foreach ( $taxonomies as $key => $value ) {
551 if ( ! taxonomy_exists( $value ) ) { // Remove from array is taxonomy not exist !
552 unset( $taxonomies[ $key ] );
553 } elseif ( true === $force_single ) {// Force single instead array ?
554 return $value;
555 }
556 }
557
558 return $taxonomies;
559 } elseif ( taxonomy_exists( $taxonomies ) ) {
560 return $taxonomies;
561 }
562
563 return 'post_tag';
564 }
565
566 /**
567 * Extended get_tags public static function that use getTerms function
568 *
569 * @param string|array $args
570 * @param string|array $taxonomy
571 *
572 * @return array
573 */
574 public static function getTags( $args = '', $taxonomy = 'post_tag', $post_type = '' ) {
575 $key = md5( maybe_serialize( $args ) . $taxonomy . (isset($args['parent_term']) ? $args['parent_term'] : '') . (isset($args['display_mode']) ? $args['display_mode'] : ''));
576
577 // Get cache if exist
578 if ( $cache = wp_cache_get( 'st_get_tags', 'simple-tags' ) ) {
579 if ( isset( $cache[ $key ] ) ) {
580 return apply_filters( 'get_tags', $cache[ $key ], $args );
581 }
582 }
583
584 // Get tags
585 if ( isset( $args['taxonomy'] ) ) {
586 $taxonomy = $args['taxonomy'];
587 }
588
589 if ( isset( $args['parent_term'] ) ) {
590 $parent_term = $args['parent_term'];
591 }
592
593 if ( isset( $args['display_mode'] ) ) {
594 $display_mode = $args['display_mode'];
595 }
596
597 if ( isset( $args['max'] ) ) {
598 $max_terms = intval( $args['max'] );
599 } else {
600 $max_terms = 20;
601 }
602
603 if ( isset( $args['hide_terms'] ) ) {
604 $hide_terms = $args['hide_terms'];
605 }
606
607 $term_args = [
608 'taxonomy' => $taxonomy,
609 'hide_empty' => false,
610 'number' => $max_terms,
611 ];
612
613 // exclude hidden terms from being queried for display at all
614 if ($hide_terms && SimpleTags_Plugin::get_option_value('enable_hidden_terms')) {
615 $hidden_terms = get_transient('taxopress_hidden_terms_' . $taxonomy);
616 if (!empty($hidden_terms) && is_array($hidden_terms)) {
617 $term_args['exclude'] = $hidden_terms;
618 }
619 }
620
621 $is_hierarchical = is_taxonomy_hierarchical($taxonomy);
622
623 if ($is_hierarchical && isset($args['parent_term'])) {
624 $parent_term = $args['parent_term'];
625 $display_mode = isset($args['display_mode']) ? $args['display_mode'] : '';
626
627 if ($parent_term === 'all') {
628 if ($display_mode === 'parents_only') {
629 $term_args['parent'] = 0;
630 } elseif ($display_mode === 'sub_terms_only') {
631 // Get all parent terms
632 $parent_terms = get_terms([
633 'taxonomy' => $taxonomy,
634 'parent' => 0,
635 'hide_empty' => false
636 ]);
637
638 $parent_ids = wp_list_pluck($parent_terms, 'term_id');
639
640 if (!empty($parent_ids)) {
641 $sub_terms = [];
642 foreach ($parent_ids as $parent_id) {
643 $terms = get_terms([
644 'taxonomy' => $taxonomy,
645 'parent' => $parent_id,
646 'hide_empty' => false,
647 'number' => $max_terms
648 ]);
649 $sub_terms = array_merge($sub_terms, $terms);
650 if (count($sub_terms) >= $max_terms) {
651 break;
652 }
653 }
654 $term_args['include'] = wp_list_pluck($sub_terms, 'term_id');
655 } else {
656 $term_args['parent'] = -1;
657 }
658 }
659 } else {
660 // Specific parent term selected
661 if ($display_mode === 'parents_only') {
662 $term_args['include'] = [$parent_term];
663 } elseif ($display_mode === 'sub_terms_only') {
664 $term_args['parent'] = $parent_term;
665 } else {
666 // Both parent and sub-terms
667 $parent_terms = get_terms([
668 'taxonomy' => $taxonomy,
669 'include' => [$parent_term],
670 'hide_empty' => false
671 ]);
672 $sub_terms = get_terms([
673 'taxonomy' => $taxonomy,
674 'parent' => $parent_term,
675 'hide_empty' => false,
676 'number' => $max_terms
677 ]);
678
679 $all_terms = array_merge($parent_terms, $sub_terms);
680 $term_args['include'] = wp_list_pluck(array_slice($all_terms, 0, $max_terms), 'term_id');
681 }
682 }
683 }
684
685 // Ensure `max_terms` is always applied
686 if (!empty($term_args['include'])) {
687 $term_args['include'] = array_slice($term_args['include'], 0, $max_terms);
688 }
689
690
691 if (isset($args['orderby'])) {
692 $term_args['orderby'] = $args['orderby'];
693 }
694 if (isset($args['order'])) {
695 $term_args['order'] = $args['order'];
696 }
697
698 if (!empty($args['limit_days'])) {
699 $recent_posts = get_posts([
700 'post_type' => $post_type ?: 'any',
701 'post_status' => 'publish',
702 'date_query' => [
703 [
704 'after' => $args['limit_days'] . ' days ago',
705 'inclusive' => false,
706 ],
707 ],
708 'fields' => 'ids',
709 'posts_per_page' => -1,
710 ]);
711 if (!empty($recent_posts)) {
712 $terms = get_terms([
713 'taxonomy' => $taxonomy,
714 'object_ids' => $recent_posts,
715 'hide_empty' => false,
716 'number' => $max_terms,
717 ]);
718 } else {
719 $terms = [];
720 }
721 } else {
722 $terms = get_terms($term_args);
723 }
724
725 if (isset($args['orderby']) && strtolower($args['orderby']) === 'random' && is_array($terms)) {
726 shuffle($terms);
727 if (isset($args['order']) && strtolower($args['order']) === 'desc') {
728 $terms = array_reverse($terms);
729 }
730 }
731 if (empty($terms)) {
732 return [];
733 }
734
735 if ($hide_terms && SimpleTags_Plugin::get_option_value('enable_hidden_terms')) {
736 $hidden_terms = get_transient('taxopress_hidden_terms_' . $taxonomy);
737 if (!empty($hidden_terms) && is_array($hidden_terms)) {
738 $hidden_terms = array_flip($hidden_terms);
739 $terms = array_filter($terms, function ($term) use ($hidden_terms) {
740 return !isset($hidden_terms[$term->term_id]);
741 });
742 }
743 }
744
745 // Cache the result
746 $cache = [];
747 $cache[$key] = $terms;
748 wp_cache_set('st_get_tags', $cache, 'simple-tags');
749
750 $terms = apply_filters('st_get_tags', $terms, $args);
751
752 return $terms;
753 }
754
755 /**
756 * Helper public static function for keep compatibility with old options TaxoPress widgets
757 *
758 * @param string $old_value
759 * @param string $key
760 *
761 * @return string
762 * @author WebFactory Ltd
763 */
764 public static function compatOldOrder( $old_value = '', $key = '' ) {
765 $return = array();
766
767 switch ( strtolower( $old_value ) ) { // count-asc/count-desc/name-asc/name-desc/random
768 case 'count-asc':
769 $return = array( 'orderby' => 'count', 'order' => 'ASC' );
770 break;
771 case 'rand':
772 case 'random':
773 $return = array( 'orderby' => 'random', 'order' => '' );
774 break;
775 case 'name-asc':
776 $return = array( 'orderby' => 'name', 'order' => 'ASC' );
777 break;
778 case 'name-desc':
779 $return = array( 'orderby' => 'name', 'order' => 'DESC' );
780 break;
781 case 'count-desc':
782 $return = array( 'orderby' => 'count', 'order' => 'DESC' );
783 break;
784 }
785
786 if ( empty( $return ) || ! isset( $return[ $key ] ) ) {
787 return $old_value;
788 }
789
790 return $return[ $key ];
791 }
792
793 /**
794 * Extended get_terms public static function support
795 * - Limit category
796 * - Limit days
797 * - Selection restrict
798 * - Min usage
799 *
800 * @param string|array $taxonomies
801 * @param string $args
802 *
803 * @return array|WP_Error
804 */
805 public static function getTerms( $taxonomies, $args = '' ) {
806 global $wpdb;
807 $empty_array = array();
808 $join_relation = false;
809
810 $single_taxonomy = false;
811 if ( ! is_array( $taxonomies ) ) {
812 $single_taxonomy = true;
813 $taxonomies = array( $taxonomies );
814 }
815
816 foreach ( (array) $taxonomies as $taxonomy ) {
817 if ( ! taxonomy_exists( $taxonomy ) ) {
818 $error = new WP_Error( 'invalid_taxonomy', __( 'Invalid Taxonomy' ) );
819
820 return $error;
821 }
822 }
823 $in_taxonomies = "'" . implode( "', '", $taxonomies ) . "'";
824
825 $defaults = array(
826 'orderby' => 'name',
827 'order' => 'ASC',
828 'hide_empty' => true,
829 'exclude' => array(),
830 'exclude_tree' => array(),
831 'include' => array(),
832 'number' => '',
833 'fields' => 'all',
834 'slug' => '',
835 'parent' => '',
836 'hierarchical' => true,
837 'child_of' => 0,
838 'get' => '',
839 'name__like' => '',
840 'pad_counts' => false,
841 'offset' => '',
842 'search' => '',
843 // TaxoPress added
844 'limit_days' => 0,
845 'category' => 0,
846 'min_usage' => 0,
847 'st_name__like' => '',
848 'post_type' => false
849 );
850
851 $args = wp_parse_args( $args, $defaults );
852
853 // Translate selection order
854 $args['orderby'] = self::compatOldOrder( $args['selectionby'], 'orderby' );
855 $args['order'] = self::compatOldOrder( $args['selection'], 'order' );
856
857 $args['number'] = absint( $args['number'] );
858 $args['offset'] = absint( $args['offset'] );
859 $args['limit_days'] = absint( $args['limit_days'] );
860 $args['min_usage'] = absint( $args['min_usage'] );
861
862 if ( ! $single_taxonomy || ! is_taxonomy_hierarchical( $taxonomies[0] ) ||
863 '' !== $args['parent']
864 ) {
865 $args['child_of'] = 0;
866 $args['hierarchical'] = false;
867 $args['pad_counts'] = false;
868 }
869
870 if ( 'all' == $args['get'] ) {
871 $args['child_of'] = 0;
872 $args['hide_empty'] = 0;
873 $args['hierarchical'] = false;
874 $args['pad_counts'] = false;
875 }
876 extract( $args, EXTR_SKIP );
877
878 if ( $child_of ) {
879 $hierarchy = _get_term_hierarchy( $taxonomies[0] );
880 if ( ! isset( $hierarchy[ $child_of ] ) ) {
881 return $empty_array;
882 }
883 }
884
885 if ( $parent ) {
886 $hierarchy = _get_term_hierarchy( $taxonomies[0] );
887 if ( ! isset( $hierarchy[ $parent ] ) ) {
888 return $empty_array;
889 }
890 }
891
892 // $args can be whatever, only use the args defined in defaults to compute the key
893 $filter_key = ( has_filter( 'list_terms_exclusions' ) ) ? serialize( $GLOBALS['wp_filter']['list_terms_exclusions'] ) : '';
894 $key = md5( serialize( compact( array_keys( $defaults ) ) ) . serialize( $taxonomies ) . $filter_key );
895 $last_changed = wp_cache_get( 'last_changed', 's-terms' );
896 if ( ! $last_changed ) {
897 $last_changed = time();
898 wp_cache_set( 'last_changed', $last_changed, 's-terms' );
899 }
900 $cache_key = "get_terms:$key:$last_changed";
901 $cache = wp_cache_get( $cache_key, 's-terms' );
902 if ( false !== $cache ) {
903 $cache = apply_filters( 'get_terms', $cache, $taxonomies, $args );
904
905 return $cache;
906 }
907
908 $_orderby = strtolower( $orderby );
909 if ( 'count' == $_orderby ) {
910 $orderby = 'tt.count';
911 }
912 if ( 'random' == $_orderby ) {
913 $orderby = 'RAND()';
914 } else if ( 'name' == $_orderby ) {
915 $orderby = 't.name';
916 } else if ( 'slug' == $_orderby ) {
917 $orderby = 't.slug';
918 } else if ( 'term_group' == $_orderby ) {
919 $orderby = 't.term_group';
920 } elseif ( empty( $_orderby ) || 'id' == $_orderby ) {
921 $orderby = 't.term_id';
922 }
923 $orderby = apply_filters( 'get_terms_orderby', $orderby, $args );
924
925 if ( ! empty( $orderby ) ) {
926 $orderby = "ORDER BY $orderby";
927 } else {
928 $order = '';
929 }
930
931 $where = '';
932 $inclusions = '';
933 if ( ! empty( $include ) ) {
934 $exclude = '';
935 $exclude_tree = '';
936 $interms = wp_parse_id_list( $include );
937 foreach ( $interms as $interm ) {
938 if ( empty( $inclusions ) ) {
939 $inclusions = ' AND ( t.term_id = ' . intval( $interm ) . ' ';
940 } else {
941 $inclusions .= ' OR t.term_id = ' . intval( $interm ) . ' ';
942 }
943 }
944 }
945
946 if ( ! empty( $inclusions ) ) {
947 $inclusions .= ')';
948 }
949 $where .= $inclusions;
950
951 $exclusions = '';
952
953 $excluded_terms = array(); // Collect all terms to exclude
954
955 if ( ! empty( $exclude_tree ) ) {
956 $excluded_trunks = wp_parse_id_list( $exclude_tree );
957 foreach ( $excluded_trunks as $extrunk ) {
958 $excluded_children = (array) get_terms( $taxonomies[0], array(
959 'child_of' => intval( $extrunk ),
960 'fields' => 'ids'
961 ) );
962 $excluded_children[] = $extrunk;
963 foreach ( $excluded_children as $exterm ) {
964 if ( empty( $exclusions ) ) {
965 $exclusions = ' AND ( t.term_id <> ' . intval( $exterm ) . ' ';
966 } else {
967 $exclusions .= ' AND t.term_id <> ' . intval( $exterm ) . ' ';
968 }
969 }
970
971 // Also collect terms for batch exclusion
972 $excluded_terms = array_merge( $excluded_terms, $excluded_children );
973 }
974 }
975
976 if ( ! empty( $exclude ) ) {
977 $exterms = wp_parse_id_list( $exclude );
978 foreach ( $exterms as $exterm ) {
979 if ( empty( $exclusions ) ) {
980 $exclusions = ' AND ( t.term_id <> ' . intval( $exterm ) . ' ';
981 } else {
982 $exclusions .= ' AND t.term_id <> ' . intval( $exterm ) . ' ';
983 }
984 }
985
986 // Also collect terms for batch exclusion
987 $excluded_terms = array_merge( $excluded_terms, $exterms );
988 }
989
990 // Batch exclusion logic with 'NOT IN'
991 $excluded_terms = array_unique( $excluded_terms );
992 if ( ! empty( $excluded_terms ) ) {
993 $excluded_terms_sql = implode( ',', array_map( 'intval', $excluded_terms ) );
994 // Batch exclusion will be combined with original exclusion logic
995 $exclusions .= " AND t.term_id NOT IN ($excluded_terms_sql)";
996 }
997
998 if ( ! empty( $exclusions ) ) {
999 $exclusions .= ')';
1000 }
1001 $exclusions = apply_filters( 'list_terms_exclusions', $exclusions, $args );
1002 $where .= $exclusions;
1003
1004 // ST Features : Restrict category
1005 if (!empty($category)) {
1006 if ( ! is_array( $taxonomies ) ) {
1007 $taxonomies = array( $taxonomies );
1008 }
1009
1010 $incategories = wp_parse_id_list( $category );
1011
1012 $taxonomies = "'" . implode( "', '", $taxonomies ) . "'";
1013 $incategories = "'" . implode( "', '", $incategories ) . "'";
1014
1015 $where .= " AND tr.object_id IN ( ";
1016 $where .= "SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id INNER JOIN $wpdb->posts as p ON tr.object_id=p.ID WHERE tt.term_id IN ($incategories) AND p.post_status='publish'";
1017 $where .= " ) ";
1018 $join_relation = true;
1019 unset( $incategories, $category );
1020 }
1021
1022 // ST Features : Limit posts date
1023 if ( (int)$limit_days > 0 ) {
1024 if($post_type){
1025 $post_type = "AND post_type = '$post_type'";
1026 }else{
1027 $post_type = '';
1028 }
1029 $where .= " AND tr.object_id IN ( ";
1030 $where .= "SELECT DISTINCT ID FROM $wpdb->posts AS p WHERE p.post_date_gmt > '" . date( 'Y-m-d H:i:s', time() - $limit_days * 86400 ) . "' $post_type";
1031 $where .= " ) ";
1032 $join_relation = true;
1033 unset( $limit_days );
1034 }else{
1035 if($post_type){
1036 $where .= " AND tr.object_id IN ( ";
1037 $where .= "SELECT DISTINCT ID FROM $wpdb->posts AS p WHERE post_type = '$post_type'";
1038 $where .= " ) ";
1039 $join_relation = true;
1040 }
1041 }
1042
1043 if ( ! empty( $slug ) ) {
1044 $slug = sanitize_title( $slug );
1045 $where .= " AND t.slug = '$slug'";
1046 }
1047
1048 if ( ! empty( $name__like ) ) {
1049 $where .= " AND t.name LIKE '{$name__like}%'";
1050 }
1051
1052 if ( '' !== $parent ) {
1053 $parent = (int) $parent;
1054 $where .= " AND tt.parent = '$parent'";
1055 }
1056
1057 // ST Features : Another way to search
1058 if ( strpos( $st_name__like, ' ' ) !== false ) {
1059
1060 $st_terms_formatted = array();
1061 $st_terms = preg_split( '/[\s,]+/', $st_name_like );
1062 foreach ( (array) $st_terms as $st_term ) {
1063 if ( empty( $st_term ) ) {
1064 continue;
1065 }
1066 $st_terms_formatted[] = "t.name LIKE '%" . like_escape( $st_term ) . "%'";
1067 }
1068
1069 $where .= " AND ( " . explode( ' OR ', $st_terms_formatted ) . " ) ";
1070 unset( $st_term, $st_terms_formatted, $st_terms );
1071
1072 } elseif ( ! empty( $st_name__like ) ) {
1073
1074 $where .= " AND t.name LIKE '%{$st_name__like}%'";
1075
1076 }
1077
1078 if(in_array($taxonomies[0], ['post_tag', 'category'])){
1079 //TODO - count not working for attachment and/or CPT ?
1080 // ST Features : Add min usage
1081 if ( $hide_empty && ! $hierarchical ) {
1082 if ( $min_usage == 0 ) {
1083 $where .= ' AND tt.count > 0';
1084 } else {
1085 $where .= $wpdb->prepare( ' AND tt.count >= %d', $min_usage );
1086 }
1087 }
1088 }
1089
1090
1091 if ( ! empty( $search ) ) {
1092 $search = like_escape( $search );
1093 $where .= " AND (t.name LIKE '%$search%')";
1094 }
1095
1096 $selects = array();
1097 switch ( $fields ) {
1098 case 'all':
1099 $selects = array( 't.*', 'tt.*' );
1100 break;
1101 case 'ids':
1102 case 'id=>parent':
1103 $selects = array( 't.term_id', 'tt.parent', 'tt.count' );
1104 break;
1105 case 'names':
1106 $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.name' );
1107 break;
1108 case 'count':
1109 $orderby = '';
1110 $order = '';
1111 $selects = array( 'COUNT(*)' );
1112 }
1113 $select_this = implode( ', ', apply_filters( 'get_terms_fields', $selects, $args ) );
1114
1115 // Add inner to relation table ?
1116 $join_relation = $join_relation == false ? '' : "INNER JOIN $wpdb->term_relationships AS tr ON tt.term_taxonomy_id = tr.term_taxonomy_id";
1117 // Query parts are individually escaped above, $taxonomies are individually checked if they exists
1118
1119
1120 // don't limit the query results when we have to descend the family tree
1121 if ( ! empty( $number ) && ! $hierarchical && empty( $child_of ) && '' == $parent ) {
1122 if ( $offset ) {
1123 $query = $wpdb->prepare("SELECT DISTINCT $select_this
1124 FROM $wpdb->terms AS t
1125 INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id
1126 $join_relation
1127 WHERE 1 = %d AND tt.taxonomy IN ($in_taxonomies)
1128 $where
1129 $orderby $order
1130 LIMIT %d, %d",
1131 1,
1132 $offset,
1133 $number );
1134 } else {
1135 $query = $wpdb->prepare("SELECT DISTINCT $select_this
1136 FROM $wpdb->terms AS t
1137 INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id
1138 $join_relation
1139 WHERE 1 = %d AND tt.taxonomy IN ($in_taxonomies)
1140 $where
1141 $orderby $order
1142 LIMIT %d",
1143 1,
1144 $number );
1145 }
1146 } else {
1147 $query = $wpdb->prepare("SELECT DISTINCT $select_this
1148 FROM $wpdb->terms AS t
1149 INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id
1150 $join_relation
1151 WHERE 1 = %d AND tt.taxonomy IN ($in_taxonomies)
1152 $where
1153 $orderby $order",
1154 1 );
1155 }
1156
1157 // GROUP BY t.term_id
1158
1159 if ( 'count' == $fields ) {
1160 $term_count = $wpdb->get_var( $query );
1161
1162 return $term_count;
1163 }
1164
1165 $terms = $wpdb->get_results( $query );
1166 if ( 'all' == $fields ) {
1167 update_term_cache( $terms );
1168 }
1169
1170 if ( empty( $terms ) ) {
1171 wp_cache_add( $cache_key, array(), 's-terms' );
1172 $terms = apply_filters( 'get_terms', array(), $taxonomies, $args );
1173
1174 return $terms;
1175 }
1176
1177 if ( $child_of ) {
1178 $children = _get_term_hierarchy( $taxonomies[0] );
1179 if ( ! empty( $children ) ) {
1180 $terms = &_get_term_children( $child_of, $terms, $taxonomies[0] );
1181 }
1182 }
1183
1184 // Update term counts to include children.
1185 if ( $pad_counts && 'all' == $fields ) {
1186 _pad_term_counts( $terms, $taxonomies[0] );
1187 }
1188
1189 // Make sure we show empty categories that have children.
1190 if ( $hierarchical && $hide_empty && is_array( $terms ) ) {
1191 foreach ( $terms as $k => $term ) {
1192 if ( ! $term->count ) {
1193 $children = _get_term_children( $term->term_id, $terms, $taxonomies[0] );
1194 if ( is_array( $children ) ) {
1195 foreach ( $children as $child ) {
1196 if ( $child->count ) {
1197 continue 2;
1198 }
1199 }
1200 }
1201
1202 // It really is empty
1203 unset( $terms[ $k ] );
1204 }
1205 }
1206 }
1207 reset( $terms );
1208
1209 $_terms = array();
1210 if ( 'id=>parent' == $fields ) {
1211 while ( $term = array_shift( $terms ) ) {
1212 $_terms[ $term->term_id ] = $term->parent;
1213 }
1214 $terms = $_terms;
1215 } elseif ( 'ids' == $fields ) {
1216 while ( $term = array_shift( $terms ) ) {
1217 $_terms[] = $term->term_id;
1218 }
1219 $terms = $_terms;
1220 } elseif ( 'names' == $fields ) {
1221 while ( $term = array_shift( $terms ) ) {
1222 $_terms[] = $term->name;
1223 }
1224 $terms = $_terms;
1225 }
1226
1227 if ( 0 < $number && intval( @count( $terms ) ) > $number ) {
1228 $terms = array_slice( $terms, $offset, $number );
1229 }
1230
1231 wp_cache_add( $cache_key, $terms, 's-terms' );
1232
1233 $terms = apply_filters( 'get_terms', $terms, $taxonomies, $args );
1234
1235 return $terms;
1236 }
1237 }
1238