PluginProbe
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms / 3.44.0
Tag, Category, and Taxonomy Manager – Autotagger Automatically Add Terms v3.44.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 3.6.2 All 177 releases
simple-tags / inc / class.client.tagcloud.php

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

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