PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.2.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.2.0
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Utils / Helper.php

Helper.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.2.0, at includes/Utils/Helper.php

678 lines 24.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Utils;
4
5 use function BetterLinksPro\Dependencies\GuzzleHttp\json_decode;
6 use function WPML\PHP\Logger\error;
7
8 class Helper extends Base {
9
10 public static function get_plugins( $plugin_basename = null ) {
11 if ( ! function_exists( 'get_plugins' ) ) {
12 include_once ABSPATH . 'wp-admin/includes/plugin.php';
13 }
14
15 $plugins = get_plugins();
16 return $plugin_basename == null ? $plugins : isset( $plugins[ $plugin_basename ] );
17 }
18
19 public static function is_plugin_active( $plugin_basename ) {
20 if ( ! function_exists( 'is_plugin_active' ) ) {
21 include_once ABSPATH . 'wp-admin/includes/plugin.php';
22 }
23
24 return is_plugin_active( $plugin_basename );
25 }
26
27 public static function get_tax( $tax = '' ) {
28 global $wp_query;
29
30 if ( is_tax( 'knowledge_base' ) ) {
31 $_taxes = $wp_query->tax_query->queried_terms;
32 if ( array_key_exists( 'doc_category', $_taxes ) ) {
33 $tax = 'doc_category';
34 } else {
35 $tax = 'knowledge_base';
36 }
37 } elseif ( is_tax( 'doc_category' ) ) {
38 $tax = 'doc_category';
39 } elseif ( is_tax( 'doc_tag' ) ) {
40 $tax = 'doc_tag';
41 }
42
43 return $tax;
44 }
45
46 public function is_templates() {
47 global $wp_query;
48 $slug = betterdocs()->settings->get( 'encyclopedia_root_slug', 'encyclopedia' );
49
50 $tax = $this->get_tax();
51 if ( is_post_type_archive( 'docs' ) || $tax === 'knowledge_base' || $tax === 'doc_category' || $tax === 'doc_tag' || is_singular( 'docs' ) || is_tax( 'glossaries' ) ) {
52 return true;
53 }
54
55 if ( isset( $wp_query->query['pagename'] ) && $wp_query->query['pagename'] === $slug ) {
56 return true;
57 }
58
59 return false;
60 }
61
62 public function is_el_templates() {
63 $_return_val = betterdocs()->editor->get( 'elementor' )->is_templates();
64
65 if ( $_return_val !== null ) {
66 return $_return_val;
67 }
68
69 $this->is_templates();
70 }
71
72 /**
73 * Which tab to show.
74 *
75 * 1. Drag and Drop UI
76 * 2. Post List UI
77 *
78 * * 1. dnd
79 * * 2. classic
80 *
81 * look into views/admin/docs-ui directory to know more.
82 *
83 * @return string
84 */
85 public static function admin_tab() {
86 $admin_ui = 'grid';
87 if ( isset( $_GET['mode'], $_GET['page'] ) && $_GET['page'] === 'betterdocs-admin' && ! empty( $_GET['mode'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
88 $admin_ui = $_GET['mode'] === 'grid' ? 'grid' : 'list'; // phpcs:ignore WordPress.Security.NonceVerification.Missing
89 }
90
91 return $admin_ui;
92 }
93
94 public static function is_active( $prev, $current, $class = 'active' ) {
95 if ( $current == $prev ) {
96 return $class;
97 }
98
99 return '';
100 }
101
102 public function get_users( $args ) {
103 $cache_key = 'betterdocs_cache_admin_user_roles';
104 $users = betterdocs()->database->get_cache( $cache_key );
105
106 if ( false === $users ) {
107 $users = get_users( $args );
108 betterdocs()->database->set_cache( $cache_key, $users );
109 }
110
111 return $users;
112 }
113
114 /**
115 * Normalize Menu Array
116 * Menu creator helper
117 *
118 * @since 2.5.0
119 *
120 * @param string $title
121 * @param string $slug
122 * @param string $cap
123 * @param array $callback
124 *
125 * @return array
126 */
127 public static function normalize_menu( $title, $slug, $cap = 'edit_docs', $callback = null, $optional = [] ) {
128 $args = [
129 'page_title' => $title,
130 'menu_title' => $title,
131 'capability' => $cap,
132 'menu_slug' => $slug
133 ];
134
135 if ( $callback != null ) {
136 $args['callback'] = $callback;
137 }
138
139 return wp_parse_args( $optional, $args );
140 }
141
142 /**
143 * Check if the current theme is a block theme.
144 *
145 * @since x.x.x
146 * @return bool
147 */
148 public function current_theme_is_fse_theme() {
149 if ( function_exists( 'wp_is_block_theme' ) ) {
150 return (bool) wp_is_block_theme();
151 }
152 if ( function_exists( 'gutenberg_is_fse_theme' ) ) {
153 return (bool) gutenberg_is_fse_theme();
154 }
155
156 return false;
157 }
158
159 protected static function is_assoc_array( $array ) {
160 return array_keys( $array ) !== range( 0, count( $array ) - 1 );
161 }
162
163 public static function merge( &$array1, &$array2 ) {
164 $merged = $array1;
165
166 foreach ( $array2 as $key => &$value ) {
167 if ( is_array( $value ) && self::is_assoc_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
168 $merged[ $key ] = self::merge( $merged[ $key ], $value );
169 } elseif ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
170 $merged[ $key ] = array_merge( $merged[ $key ], $value );
171 } else {
172 $merged[ $key ] = $value;
173 }
174 }
175
176 return $merged;
177 }
178
179 public static function get_custom_excerpt( $content, $numOfWords ) {
180 $content = strip_shortcodes( $content );
181 $content = wp_strip_all_tags( $content );
182 $words = explode( ' ', $content );
183 $excerptWords = array_slice( $words, 0, $numOfWords );
184 $excerpt = implode( ' ', $excerptWords );
185 if ( count( $words ) > $numOfWords ) {
186 $excerpt .= '...';
187 }
188 return $excerpt;
189 }
190
191 /**
192 * Get current language from various multilingual plugins
193 *
194 * @return string|null Current language code
195 */
196 public static function get_current_language() {
197 $current_language = null;
198
199 // WPML Support
200 if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ) {
201 global $sitepress;
202 if ( $sitepress && $sitepress->is_setup_complete() ) {
203 $current_language = defined( 'ICL_LANGUAGE_CODE' ) ? ICL_LANGUAGE_CODE : $sitepress->get_current_language();
204 }
205 }
206 // Polylang Support
207 elseif ( function_exists( 'pll_current_language' ) ) {
208 $current_language = pll_current_language();
209 }
210 // qTranslate-X Support
211 elseif ( function_exists( 'qtranxf_getLanguage' ) ) {
212 $current_language = qtranxf_getLanguage();
213 }
214 // Weglot Support
215 elseif ( function_exists( 'weglot_get_current_language' ) ) {
216 $current_language = weglot_get_current_language();
217 }
218 // TranslatePress Support
219 elseif ( class_exists( 'TRP_Translate_Press' ) && function_exists( 'trp_get_current_language' ) ) {
220 $current_language = trp_get_current_language();
221 }
222
223 return $current_language;
224 }
225
226 /**
227 * Check if any multilingual plugin is active
228 *
229 * @return bool
230 */
231 public static function is_multilingual_active() {
232 return is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ||
233 function_exists( 'pll_current_language' ) ||
234 function_exists( 'qtranxf_getLanguage' ) ||
235 function_exists( 'weglot_get_current_language' ) ||
236 ( class_exists( 'TRP_Translate_Press' ) && function_exists( 'trp_get_current_language' ) );
237 }
238
239 /**
240 * Check if we should apply language filtering
241 * Only apply on frontend or when specifically requested
242 *
243 * @return bool
244 */
245 public static function should_apply_language_filtering() {
246 // Don't apply language filtering in admin context unless it's a frontend request
247 if ( is_admin() ) {
248 // Allow language filtering for REST API requests that are frontend-facing
249 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
250 // Check if this is a frontend REST request (not admin)
251 $request_uri = $_SERVER['REQUEST_URI'] ?? '';
252 // Don't filter admin REST requests for glossaries management
253 if ( strpos( $request_uri, '/wp/v2/glossaries' ) !== false ) {
254 return false; // Don't filter admin glossaries management
255 }
256 }
257 return false; // Don't filter other admin requests
258 }
259
260 // Apply filtering on frontend
261 return true;
262 }
263
264 public static function get_current_letter_docs( $current_letter, $limit = '' ) {
265 global $wpdb;
266
267 // Check if the encyclopedia_prefix parameter is set
268
269 $encyclopeia_suorce = betterdocs()->settings->get( 'encyclopedia_source', 'docs' );
270 $enable_glossaries = betterdocs()->settings->get( 'enable_glossaries', false );
271 $encyclopedia_root_slug = betterdocs()->settings->get( 'encyclopedia_root_slug', 'encyclopdia' );
272
273 // if($enable_glossaries && $encyclopeia_suorce === 'glossaries'){
274 if ( $enable_glossaries && $encyclopeia_suorce === 'glossaries' ) {
275 $lang_join = '';
276 $lang_where = '';
277
278 // Add language filtering if multilingual plugin is active and we should apply filtering
279 $current_language = self::get_current_language();
280 if ( $current_language && self::is_multilingual_active() && self::should_apply_language_filtering() ) {
281 // For WPML, use icl_translations table
282 if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ) {
283 $lang_join = " LEFT JOIN {$wpdb->prefix}icl_translations icl_t ON icl_t.element_id = t.term_id AND icl_t.element_type = 'tax_glossaries'";
284 $lang_where = " AND (icl_t.language_code = '$current_language' OR icl_t.language_code IS NULL)";
285 }
286 // For Polylang, use term_relationships with language taxonomy
287 elseif ( function_exists( 'pll_current_language' ) ) {
288 $lang_join = " LEFT JOIN {$wpdb->term_relationships} tr ON t.term_id = tr.object_id LEFT JOIN {$wpdb->term_taxonomy} tt_lang ON tr.term_taxonomy_id = tt_lang.term_taxonomy_id AND tt_lang.taxonomy = 'language' LEFT JOIN {$wpdb->terms} t_lang ON tt_lang.term_id = t_lang.term_id";
289 $lang_where = " AND (t_lang.slug = '$current_language' OR t_lang.slug IS NULL)";
290 }
291 }
292
293 $query = "
294 SELECT
295 t.term_id,
296 t.name AS post_title,
297 t.slug as slug,
298 '' AS post_excerpt,
299 CONCAT('" . get_home_url() . "/$encyclopedia_root_slug/', t.slug) AS permalink,
300 tt.description AS post_content,
301 JSON_OBJECT(
302 'status', COALESCE(MAX(CASE WHEN m.meta_key = 'status' THEN m.meta_value END), ''),
303 'glossary_term_description', COALESCE(MAX(CASE WHEN m.meta_key = 'glossary_term_description' THEN m.meta_value END), '')
304 ) AS meta_data
305 FROM
306 {$wpdb->terms} t
307 INNER JOIN
308 {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
309 LEFT JOIN
310 {$wpdb->termmeta} m ON t.term_id = m.term_id
311 $lang_join
312 WHERE
313 tt.taxonomy = 'glossaries'
314 AND
315 SUBSTRING(t.name, 1, 1) = %s
316 $lang_where
317 GROUP BY
318 t.term_id
319 ORDER BY
320 t.name ASC
321 $limit
322 ";
323 } else {
324 $lang_join = '';
325 $lang_where = '';
326
327 // Add language filtering for docs if multilingual plugin is active and we should apply filtering
328 $current_language = self::get_current_language();
329 if ( $current_language && self::is_multilingual_active() && self::should_apply_language_filtering() ) {
330 // For WPML, use icl_translations table
331 if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ) {
332 $lang_join = " LEFT JOIN {$wpdb->prefix}icl_translations icl_t ON icl_t.element_id = {$wpdb->posts}.ID AND icl_t.element_type = 'post_docs'";
333 $lang_where = " AND (icl_t.language_code = '$current_language' OR icl_t.language_code IS NULL)";
334 }
335 // For Polylang, use term_relationships with language taxonomy
336 elseif ( function_exists( 'pll_current_language' ) ) {
337 $lang_join = " LEFT JOIN {$wpdb->term_relationships} tr ON {$wpdb->posts}.ID = tr.object_id LEFT JOIN {$wpdb->term_taxonomy} tt_lang ON tr.term_taxonomy_id = tt_lang.term_taxonomy_id AND tt_lang.taxonomy = 'language' LEFT JOIN {$wpdb->terms} t_lang ON tt_lang.term_id = t_lang.term_id";
338 $lang_where = " AND (t_lang.slug = '$current_language' OR t_lang.slug IS NULL)";
339 }
340 }
341
342 $query = "
343 SELECT ID, post_title, post_excerpt, guid, post_content
344 FROM {$wpdb->posts}
345 $lang_join
346 WHERE post_type = 'docs'
347 AND post_status = 'publish'
348 AND SUBSTRING(post_title, 1, 1) = %s
349 $lang_where
350 ORDER BY post_date DESC
351 $limit
352 ";
353 }
354
355 $current_letter_docs = $wpdb->get_results( $wpdb->prepare( $query, $current_letter ), ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
356
357 return $current_letter_docs;
358 }
359
360 public static function docs_sort_by_letter( $limit = 10 ) {
361 global $wpdb;
362 $enable_non_latin = betterdocs()->settings->get( 'encyclopedia_enable_non_latin' );
363 $script = betterdocs()->settings->get( 'encyclopedia_non_latin_option' );
364 $letters = Helper::get_character_range( $enable_non_latin, $script );
365
366 $docs_by_letter = [];
367 $encyclopeia_suorce = betterdocs()->settings->get( 'encyclopedia_source', 'docs' );
368 $enable_glossaries = betterdocs()->settings->get( 'enable_glossaries', false );
369
370 foreach ( $letters as $letter ) {
371 $posts = self::get_current_letter_docs( $letter, "LIMIT $limit" );
372
373 if ( is_array( $posts ) && ! empty( $posts ) ) {
374 foreach ( $posts as $post ) {
375 $description = isset($post['meta_data']) ? \json_decode( $post['meta_data'], true ) : '';
376 $glossary_term_description = $description['glossary_term_description'] ?? '';
377
378 // Remove any <p> tags or other unwanted HTML tags
379 $glossary_term_description = strip_tags( $glossary_term_description );
380 $post_excerpt = strip_tags( $post['post_excerpt'] ?? '' );
381
382 // Prepare post data
383 if ( $enable_glossaries && $encyclopeia_suorce === 'glossaries' ) {
384 // For glossaries
385 $post_data = [
386 'id' => $post['term_id'] ?? '',
387 'post_title' => $post['post_title'] ?? '',
388 'post_excerpt' => ! empty( $post_excerpt )
389 ? $post_excerpt
390 : ( ! empty( $glossary_term_description )
391 ? self::get_custom_excerpt( $glossary_term_description, 15 )
392 : self::get_custom_excerpt( strip_tags( $post['post_content'] ?? '' ), 15 ) ),
393 'permalink' => isset( $post['slug'] ) ? get_term_link( $post['slug'], 'glossaries' ) : ''
394 ];
395 } else {
396 // For docs
397 $post_data = [
398 'id' => $post['ID'] ?? '',
399 'post_title' => $post['post_title'] ?? '',
400 'post_excerpt' => ! empty( $post_excerpt )
401 ? $post_excerpt
402 : self::get_custom_excerpt( strip_tags( $post['post_content'] ?? '' ), 15 ),
403 'permalink' => isset( $post['ID'] ) ? get_the_permalink( $post['ID'] ) : ''
404 ];
405 }
406
407 $docs_by_letter[$letter][] = $post_data;
408 }
409 }
410 }
411
412 return $docs_by_letter;
413 }
414
415 public static function get_glossaries() {
416 global $wpdb;
417
418 $lang_join = '';
419 $lang_where = '';
420
421 // Add language filtering if multilingual plugin is active and we should apply filtering
422 $current_language = self::get_current_language();
423 if ( $current_language && self::is_multilingual_active() && self::should_apply_language_filtering() ) {
424 // For WPML, use icl_translations table
425 if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ) {
426 $lang_join = " LEFT JOIN {$wpdb->prefix}icl_translations icl_t ON icl_t.element_id = t.term_id AND icl_t.element_type = 'tax_glossaries'";
427 $lang_where = " AND (icl_t.language_code = '$current_language' OR icl_t.language_code IS NULL)";
428 }
429 // For Polylang, use term_relationships with language taxonomy
430 elseif ( function_exists( 'pll_current_language' ) ) {
431 $lang_join = " LEFT JOIN {$wpdb->term_relationships} tr ON t.term_id = tr.object_id LEFT JOIN {$wpdb->term_taxonomy} tt_lang ON tr.term_taxonomy_id = tt_lang.term_taxonomy_id AND tt_lang.taxonomy = 'language' LEFT JOIN {$wpdb->terms} t_lang ON tt_lang.term_id = t_lang.term_id";
432 $lang_where = " AND (t_lang.slug = '$current_language' OR t_lang.slug IS NULL)";
433 }
434 }
435
436 $query = "
437 SELECT t.name
438 FROM {$wpdb->terms} t
439 INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
440 $lang_join
441 WHERE tt.taxonomy = 'glossaries'
442 $lang_where
443 ORDER BY t.name ASC
444 ";
445
446 $glossaries = $wpdb->get_col( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
447
448 return $glossaries;
449 }
450
451 /**
452 * Determine live search template layout, when live search is not selected from customizer(this will work when live search template is not selected from customizer)
453 *
454 * @param string $layout
455 * @return string $layout
456 */
457 public static function determine_search_layout( $layout ) {
458 if ( $layout ) {
459 return $layout;
460 }
461
462 $search_layout = betterdocs()->customizer->defaults->get( 'betterdocs_search_layout_select' );
463 $docs_layout = betterdocs()->customizer->defaults->get( 'betterdocs_docs_layout_select' );
464 $archive_page_layout = betterdocs()->customizer->defaults->get( 'betterdocs_archive_layout_select' );
465 $single_layout = betterdocs()->customizer->defaults->get( 'betterdocs_single_layout_select' );
466
467 if ( is_post_type_archive( 'docs' ) ) {
468 if ( $docs_layout != "layout-7" && ! $search_layout ) {
469 $layout = 'layout-1';
470 } else if ( $docs_layout == 'layout-7' && ! $search_layout ) {
471 $layout = 'layout-2';
472 }
473 } else if ( is_tax( 'doc_tag' ) && ! $search_layout ) {
474 $layout = 'layout-1';
475 } else if ( is_tax( 'doc_category' ) ) {
476 if ( $archive_page_layout != 'layout-7' && $archive_page_layout != 'layout-8' && ! $search_layout ) {
477 $layout = 'layout-1';
478 } else if ( ( $archive_page_layout == 'layout-7' && ! $search_layout ) || ( $archive_page_layout == 'layout-8' && ! $search_layout ) ) {
479 $layout = 'layout-2';
480 }
481 } else if ( is_singular( 'docs' ) ) {
482 if ( $single_layout != 'layout-8' && $single_layout != 'layout-9' && ! $search_layout ) {
483 $layout = 'layout-1';
484 } else if ( ( $single_layout == 'layout-8' && ! $search_layout ) || ( $single_layout == 'layout-9' && ! $search_layout ) ) {
485 $layout = 'layout-2';
486 }
487 }
488
489 return $layout;
490 }
491 public static function mb_ord_fallback( $char ) {
492 $code = unpack( 'N', mb_convert_encoding( $char, 'UCS-4BE', 'UTF-8' ) );
493 return $code[1];
494 }
495
496 public static function mb_chr_fallback( $code ) {
497 return mb_convert_encoding( pack( 'N', $code ), 'UTF-8', 'UCS-4BE' );
498 }
499
500 public static function unicodeRange( $start, $end ) {
501 $range = [];
502 for ( $i = self::mb_ord_fallback( $start ); $i <= self::mb_ord_fallback( $end ); $i++ ) {
503 $range[] = self::mb_chr_fallback( $i );
504 }
505 return $range;
506 }
507
508 public static function get_character_range( $enable_non_latin, $script ) {
509 if ( $enable_non_latin ) {
510 switch ( $script ) {
511 case 'arabic':
512 return self::unicodeRange( 'ء', 'ي' );
513 case 'cyrillic':
514 return self::unicodeRange( 'А', 'Я' );
515 case 'hebrew':
516 return self::unicodeRange( 'א', 'ת' );
517 case 'greek':
518 return self::unicodeRange( 'Α', 'Ω' );
519 default:
520 return range( 'A', 'Z' );
521 }
522 }
523
524 return range( 'A', 'Z' );
525 }
526
527 public static function get_the_top_most_parent( $term_id ) {
528 while ( $term_id != 0 ) {
529 $parent_id = wp_get_term_taxonomy_parent_id( $term_id, 'doc_category' );
530
531 if ( $parent_id == 0 ) {
532 break;
533 }
534
535 $term_id = $parent_id;
536 }
537 return $term_id;
538 }
539
540 public static function get_highest_docs_term() {
541 $terms = get_terms( [
542 'taxonomy' => 'doc_category', // Change to your desired taxonomy
543 'hide_empty' => true, // Only show terms with posts
544 'orderby' => 'count', // Order by post count
545 'order' => 'DESC', // Descending order
546 'number' => 1 // Get only the top term
547 ] );
548 return isset( $terms[0] ) ? $terms[0] : [];
549 }
550
551 public static function delete_specific_faq_posts_by_faq_category( $term_id ) {
552 $args = [
553 'post_type' => 'betterdocs_faq',
554 'posts_per_page' => -1,
555 'tax_query' => [
556 [
557 'taxonomy' => 'betterdocs_faq_category',
558 'field' => 'id',
559 'terms' => $term_id,
560 'operator' => 'IN'
561 ]
562 ],
563 'fields' => 'ids'
564 ];
565
566 $query = new \WP_Query( $args );
567
568 if ( $query->have_posts() ) {
569 foreach ( $query->posts as $doc_id ) {
570 wp_delete_post( $doc_id, true );
571 }
572 }
573 }
574
575 /**
576 * Function To Normalize Repeater Field For Quick Builder
577 *
578 * @param array $fields
579 * @param array $include_field_keys
580 *
581 * @return array
582 */
583 public static function normalize_repeater_field( $fields, $include_field_keys = [] ) {
584 if( empty( $include_field_keys ) ) {
585 return $fields;
586 }
587
588 $normalized_fields = [];
589
590 foreach( $fields as $field ) {
591 foreach( $include_field_keys as $field_key ) {
592 if( ! isset( $normalized_fields[$field_key] ) ) {
593 $normalized_fields[$field_key] = isset( $field[$field_key] ) && ! empty( $field[$field_key] ) ? $field[$field_key] : [];
594 } else {
595 array_push( $normalized_fields[$field_key], ...( isset( $field[$field_key] ) && ! empty( $field[$field_key] ) ? $field[$field_key] : [] ) );
596 $normalized_fields[$field_key] = array_unique( $normalized_fields[$field_key] );
597 }
598 }
599 }
600
601 return $normalized_fields;
602 }
603
604 public static function get_local_plugin_data( $basename = '' ) {
605 if ( empty( $basename ) ) {
606 return false;
607 }
608
609 if ( !function_exists( 'get_plugins' ) ) {
610 include_once ABSPATH . 'wp-admin/includes/plugin.php';
611 }
612
613 $plugins = get_plugins();
614
615 if ( !isset( $plugins[ $basename ] ) ) {
616 return false;
617 }
618
619 return $plugins[ $basename ];
620 }
621
622 /**
623 * Get default file icon based on programming language
624 *
625 * @param string $language Programming language identifier
626 * @return string Emoji icon for the language
627 */
628 public static function get_file_icon_by_language( $language ) {
629 $icons = [
630 'javascript' => '📄',
631 'typescript' => '📘',
632 'jsx' => '⚛️',
633 'tsx' => '⚛️',
634 'html' => '🌐',
635 'css' => '🎨',
636 'scss' => '🎨',
637 'sass' => '🎨',
638 'less' => '🎨',
639 'php' => '🐘',
640 'python' => '🐍',
641 'java' => '',
642 'csharp' => '🔷',
643 'cpp' => '⚙️',
644 'c' => '⚙️',
645 'ruby' => '💎',
646 'go' => '🐹',
647 'rust' => '🦀',
648 'swift' => '🦉',
649 'kotlin' => '🎯',
650 'sql' => '🗃️',
651 'json' => '📋',
652 'yaml' => '📋',
653 'xml' => '📄',
654 'markdown' => '📝',
655 'bash' => '💻',
656 'shell' => '💻',
657 'powershell' => '💻',
658 'dockerfile' => '🐳',
659 ];
660
661 return isset( $icons[$language] ) ? $icons[$language] : '📄';
662 }
663
664 public function is_tag_enabled() {
665 global $post;
666 $product_terms = wp_get_object_terms( $post->ID, 'doc_tag' );
667 $enable_tags = betterdocs()->settings->get( 'enable_tags', false );
668 return ! empty( $product_terms ) && $enable_tags;
669 }
670
671 public static function get_max_doc_category_order_from_term_meta() {
672 global $wpdb;
673 $sql = $wpdb->prepare( "SELECT MAX(CAST(meta_value AS UNSIGNED)) AS max FROM {$wpdb->prefix}termmeta WHERE meta_key = %s ", 'doc_category_order');
674 $result = $wpdb->get_var($sql);
675 return $result;
676 }
677 }
678