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
← All changes | includes/Utils/Helper.php +626 -117 3.4.24.2.0 View file →
@@ -1,168 +1,677 @@
1 1 <?php
2 2
3 3 namespace WPDeveloper\BetterDocs\Utils;
4 4
5 +use function BetterLinksPro\Dependencies\GuzzleHttp\json_decode;
6 +use function WPML\PHP\Logger\error;
7 +
5 8 class Helper extends Base {
6 9
7 - public static function get_plugins( $plugin_basename = null ) {
8 - if ( ! function_exists( 'get_plugins' ) ) {
9 - include_once ABSPATH . 'wp-admin/includes/plugin.php';
10 - }
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 + }
11 14
12 - $plugins = get_plugins();
13 - return $plugin_basename == null ? $plugins : isset( $plugins[$plugin_basename] );
14 - }
15 + $plugins = get_plugins();
16 + return $plugin_basename == null ? $plugins : isset( $plugins[ $plugin_basename ] );
17 + }
15 18
16 - public static function is_plugin_active( $plugin_basename ) {
17 - if ( ! function_exists( 'is_plugin_active' ) ) {
18 - include_once ABSPATH . 'wp-admin/includes/plugin.php';
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 + }
19 410 }
20 411
21 - return is_plugin_active( $plugin_basename );
412 + return $docs_by_letter;
22 413 }
23 414
24 - public static function get_tax( $tax = '' ) {
25 - global $wp_query;
415 + public static function get_glossaries() {
416 + global $wpdb;
26 417
27 - if ( is_tax( 'knowledge_base' ) ) {
28 - $_taxes = $wp_query->tax_query->queried_terms;
29 - if ( array_key_exists( "doc_category", $_taxes ) ) {
30 - $tax = 'doc_category';
31 - } else {
32 - $tax = 'knowledge_base';
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)";
33 428 }
34 - } elseif ( is_tax( 'doc_category' ) ) {
35 - $tax = 'doc_category';
36 - } elseif ( is_tax( 'doc_tag' ) ) {
37 - $tax = 'doc_tag';
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 + }
38 434 }
39 435
40 - return $tax;
41 - }
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 + ";
42 445
43 - public function is_templates() {
44 - $tax = $this->get_tax();
45 - if ( is_post_type_archive( 'docs' ) || $tax === 'knowledge_base' || $tax === 'doc_category' || $tax === 'doc_tag' || is_singular( 'docs' ) ) {
46 - return true;
47 - }
446 + $glossaries = $wpdb->get_col( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
48 447
49 - return false;
50 - }
448 + return $glossaries;
449 + }
51 450
52 - public function is_el_templates() {
53 - $_return_val = betterdocs()->editor->get( 'elementor' )->is_templates();
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 + }
54 461
55 - if ( $_return_val !== null ) {
56 - return $_return_val;
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 + }
57 487 }
58 488
59 - $this->is_templates();
489 + return $layout;
60 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 + }
61 495
62 - /**
63 - * Which tab to show.
64 - *
65 - * 1. Drag and Drop UI
66 - * 2. Post List UI
67 - *
68 - * * 1. dnd
69 - * * 2. classic
70 - *
71 - * look into views/admin/docs-ui directory to know more.
72 - *
73 - * @return string
74 - */
75 - public static function admin_tab() {
76 - $admin_ui = 'grid';
77 - if ( isset( $_GET['mode'], $_GET['page'] ) && $_GET['page'] === 'betterdocs-admin' && ! empty( $_GET['mode'] ) ) {
78 - $admin_ui = $_GET['mode'] === 'grid' ? 'grid' : 'list';
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 );
79 504 }
80 -
81 - return $admin_ui;
505 + return $range;
82 506 }
83 507
84 - public static function is_active( $prev, $current, $class = 'active' ) {
85 - if ( $current == $prev ) {
86 - return $class;
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 + }
87 522 }
88 523
89 - return '';
524 + return range( 'A', 'Z' );
90 525 }
91 526
92 - public function get_users( $args ) {
93 - $cache_key = 'betterdocs_cache_admin_user_roles';
94 - $users = betterdocs()->database->get_cache( $cache_key );
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' );
95 530
96 - if ( false === $users ) {
97 - $users = get_users( $args );
98 - betterdocs()->database->set_cache( $cache_key, $users );
531 + if ( $parent_id == 0 ) {
532 + break;
533 + }
534 +
535 + $term_id = $parent_id;
99 536 }
537 + return $term_id;
538 + }
100 539
101 - return $users;
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] : [];
102 549 }
103 550
104 - /**
105 - * Normalize Menu Array
106 - * Menu creator helper
107 - *
108 - * @since 2.5.0
109 - *
110 - * @param string $title
111 - * @param string $slug
112 - * @param string $cap
113 - * @param array $callback
114 - *
115 - * @return array
116 - */
117 - public static function normalize_menu( $title, $slug, $cap = 'edit_docs', $callback = null ) {
551 + public static function delete_specific_faq_posts_by_faq_category( $term_id ) {
118 552 $args = [
119 - 'page_title' => $title,
120 - 'menu_title' => $title,
121 - 'capability' => $cap,
122 - 'menu_slug' => $slug
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'
123 564 ];
124 565
125 - if ( $callback != null ) {
126 - $args['callback'] = $callback;
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 + }
127 572 }
573 + }
128 574
129 - return $args;
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 ];
130 620 }
131 621
132 622 /**
133 - * Check if the current theme is a block theme.
623 + * Get default file icon based on programming language
134 624 *
135 - * @since x.x.x
136 - * @return bool
625 + * @param string $language Programming language identifier
626 + * @return string Emoji icon for the language
137 627 */
138 - public function current_theme_is_fse_theme() {
139 - if ( function_exists( 'wp_is_block_theme' ) ) {
140 - return (bool) wp_is_block_theme();
141 - }
142 - if ( function_exists( 'gutenberg_is_fse_theme' ) ) {
143 - return (bool) gutenberg_is_fse_theme();
144 - }
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 + ];
145 660
146 - return false;
661 + return isset( $icons[$language] ) ? $icons[$language] : '📄';
147 662 }
148 663
149 - protected static function is_assoc_array( $array ) {
150 - return array_keys( $array ) !== range( 0, count( $array ) - 1 );
151 - }
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 + }
152 670
153 - public static function merge( &$array1, &$array2 ) {
154 - $merged = $array1;
155 -
156 - foreach ( $array2 as $key => &$value ) {
157 - if ( is_array( $value ) && self::is_assoc_array( $value ) && isset( $merged[$key] ) && is_array( $merged[$key] ) ) {
158 - $merged[$key] = self::merge( $merged[$key], $value );
159 - } elseif ( is_array( $value ) && isset( $merged[$key] ) && is_array( $merged[$key] ) ) {
160 - $merged[$key] = array_merge( $merged[$key], $value );
161 - } else {
162 - $merged[$key] = $value;
163 - }
164 - }
165 -
166 - return $merged;
167 - }
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 + }
168 677 }