PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.3.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.3.2
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 / Core / Query.php

Query.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 3.3.2, at includes/Core/Query.php

811 lines 26.8 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\Core;
4
5 use WP_Query;
6 use WPDeveloper\BetterDocs\Utils\Base;
7 use WPDeveloper\BetterDocs\Utils\Database;
8 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
9
10 class Query extends Base {
11 private $container;
12 protected $database;
13 protected $settings;
14
15 public function __construct( Container $container, Database $database, Settings $settings ) {
16 $this->container = $container;
17 $this->database = $database;
18 $this->settings = $settings;
19
20 add_action( 'parse_term_query', [$this, 'parse_term_query'] );
21 // add_action( 'parse_query', [$this, 'parse_query'], 1 );
22 add_action( 'pre_get_posts', [$this, 'pre_get_posts'], 1 );
23
24 /**
25 * These below filters are hooked for navigation only.
26 *
27 * For old version of this portion.
28 * @see `betterdocs_single_post_nav` filter
29 *
30 * For details:
31 * @see https://developer.wordpress.org/reference/functions/get_next_post/
32 * @see https://developer.wordpress.org/reference/functions/get_previous_post/
33 *
34 * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
35 */
36 add_filter( 'get_next_post_where', [$this, 'next_post_where'], 99, 5 );
37 add_filter( 'get_previous_post_where', [$this, 'previous_post_where'], 99, 5 );
38
39 $this->init();
40
41 /**
42 * Modify Popular Docs Query (For Shortcode & Widget)
43 */
44 add_filter( 'posts_clauses', [$this, 'mod_query_popular_docs'], 10, 2 );
45 }
46
47 public function mod_query_popular_docs( $clauses, $wp_query ) {
48 if ( isset( $wp_query->query['meta_key'] ) ) {
49 if ( $wp_query->query['meta_key'] == '_betterdocs_meta_views' ) {
50 global $wpdb;
51 $order = isset( $wp_query->query['order'] ) ? $wp_query->query['order'] : '';
52 $order_by_query = ( $order == 'ASC' || $order == 'DESC' ) ? "SUM({$wpdb->prefix}betterdocs_analytics.impressions) {$order}" : ( $order == 'MODIFIED' ? "{$wpdb->prefix}posts.post_modified_gmt DESC" : "{$wpdb->prefix}posts.post_date_gmt DESC" );
53 $clauses['join'] = "JOIN {$wpdb->prefix}betterdocs_analytics ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}betterdocs_analytics.post_id";
54 $clauses['where'] = "AND ( ( {$wpdb->prefix}posts.post_type = 'docs' ) AND ( {$wpdb->prefix}posts.post_status = 'publish' OR {$wpdb->prefix}posts.post_status = 'future' OR {$wpdb->prefix}posts.post_status = 'draft' OR {$wpdb->prefix}posts.post_status = 'pending' OR {$wpdb->prefix}posts.post_status = 'private' ) )";
55 $clauses['orderby'] = $order_by_query;
56 $clauses['groupby'] = "{$wpdb->prefix}betterdocs_analytics.post_id";
57 }
58 }
59 return $clauses;
60 }
61
62 public function init() {
63 }
64
65 public function parse_term_query( $term_query ) {
66 if ( empty( $term_query->query_vars['taxonomy'] ) ) {
67 return;
68 }
69
70 if ( ! in_array( 'doc_category', $term_query->query_vars['taxonomy'], true ) ) {
71 return;
72 }
73
74 global $current_screen;
75
76 if ( $current_screen == null ) {
77 return;
78 }
79
80 if ( $current_screen->taxonomy !== 'doc_category' || $current_screen->id != 'edit-doc_category' ) {
81 return;
82 }
83
84 $term_query->query_vars['meta_query'] = [[
85 'key' => 'doc_category_order',
86 'type' => 'NUMERIC'
87 ]];
88
89 $term_query->query_vars['orderby'] = 'meta_value_num';
90 }
91
92 public function parse_query( &$query ) {
93 // dump( is_single(), $query->query_vars );
94 // if ( is_single() && isset( $query->query_vars['post_type'] ) && $query->query_vars['post_type'] == 'docs' ) {
95 // $query->is_single = false;
96 // $query->is_archive = true;
97 // $query->set( 'knowledge_base', $query->query_vars['docs'] );
98 // }
99 }
100
101 public function pre_get_posts( &$query ) {
102 if ( is_admin() || ! $query->is_main_query() ) {
103 return;
104 }
105
106 if ( is_tax( 'doc_category' ) ) {
107 $query->set( 'post_type', 'docs' );
108 $query->set( 'posts_per_archive_page', -1 );
109
110 $term = get_term_by( 'slug', $query->get( 'doc_category', '' ), 'doc_category' );
111
112 $post__in = $this->get_docs_order_by_terms( $term->term_id );
113 if ( ! empty( $post__in ) ) {
114 $query->set( 'orderby', 'post__in' );
115 $query->set( 'post__in', $post__in );
116 }
117
118 // if ( ! empty( $query->query_vars['knowledge_base'] ) ) {
119 // $query->betterdocs_terms = get_terms( [
120 // 'taxonomy' => 'doc_category',
121 // 'parent' => 0,
122 // 'hide_empty' => true,
123 // 'meta_query' => [
124 // 'relation' => 'OR',
125 // [
126 // 'key' => 'doc_category_knowledge_base',
127 // 'value' => $query->query_vars['knowledge_base'],
128 // 'compare' => 'LIKE'
129 // ]
130 // ]
131 // ] );
132 // }
133 }
134
135 // dump( is_archive(), $query->query_vars );
136 }
137
138 /**
139 * Get docs orders by term_id.
140 *
141 * @since 2.5.0
142 * @param int $term_id
143 *
144 * @return array
145 */
146 public function get_docs_order_by_terms( $term_id ) {
147 global $wpdb;
148 $_docs_order = get_term_meta( $term_id, '_docs_order', true );
149 $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = %d", $term_id );
150 $query_key = "docs_order_by_terms_{$term_id}_" . md5( $query );
151
152 if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
153 return $results;
154 }
155
156 if ( ! empty( $_docs_order ) ) {
157 $_docs_order = explode( ',', $_docs_order );
158 $new_ids = [];
159
160 $results = $wpdb->get_results( $query );
161
162 if ( is_array( $results ) && ! empty( $results ) ) {
163 $object_ids = array_filter( $results, function ( $value ) use ( $_docs_order ) {
164 return ! in_array( $value->object_id, $_docs_order );
165 } );
166
167 if ( ! empty( $object_ids ) ) {
168 array_walk( $object_ids, function ( $value ) use ( &$new_ids ) {
169 $new_ids[] = $value->object_id;
170 } );
171 }
172 }
173
174 $_docs_order = array_merge( $new_ids, $_docs_order );
175 $this->database->set_cache( $query_key, $_docs_order, 1 );
176
177 return $_docs_order;
178 }
179
180 return [];
181 }
182
183 /**
184 * For determine the next post ID
185 *
186 * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
187 *
188 * @param mixed $where
189 * @param mixed $in_same_term
190 * @param mixed $excluded_terms
191 * @param mixed $taxonomy
192 * @param mixed $post
193 * @return mixed
194 */
195 public function next_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
196 return $this->get_adjacent_post_id( 'next', $where, $post, $taxonomy );
197 }
198
199 /**
200 * For determine the previous post ID
201 *
202 * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
203 *
204 * @param mixed $where
205 * @param mixed $in_same_term
206 * @param mixed $excluded_terms
207 * @param mixed $taxonomy
208 * @param mixed $post
209 * @return mixed
210 */
211 public function previous_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
212 return $this->get_adjacent_post_id( 'previous', $where, $post, $taxonomy );
213 }
214
215 /**
216 * Get where clause for next/previous post ID mainly used in post navigation.
217 *
218 * @see `previous_post_where` and `next_post_where` methods.
219 *
220 * @param mixed $adjacent
221 * @param mixed $where
222 * @param mixed $post
223 * @param mixed $taxonomy
224 * @return mixed
225 */
226 private function get_adjacent_post_id( $adjacent, $where, $post, $taxonomy ) {
227 if ( $taxonomy !== 'doc_category' ) {
228 return $where;
229 }
230
231 $_id = null;
232 $_terms = get_the_terms( $post->ID, 'doc_category' );
233 if ( empty( $_terms ) ) {
234 return $where;
235 }
236
237 global $wp_query, $wpdb;
238
239 $_docs_order = $this->get_docs_order_by_terms( $_terms[0]->term_id );
240
241 $_where = explode( 'AND', $where );
242 if ( is_array( $_where ) ) {
243 // $_where[0] = str_replace( 'WHERE ', '', $_where[0] );
244 unset( $_where[0] );
245 $_where = implode( 'AND', $_where );
246 }
247
248 $_orderby = $this->settings->get( 'alphabetically_order_post', 'betterdocs_order' );
249 $_order = $this->settings->get( 'docs_order', 'ASC' );
250 $_docs_order = $_orderby === 'betterdocs_order' ? $_docs_order : [];
251
252 if ( empty( $_docs_order ) ) {
253 $statuses = [ 'publish' ];
254
255 if( is_user_logged_in() ) {
256 $statuses[] = 'private';
257 }
258
259 $_args = [
260 'post_status' => $statuses
261 ];
262 if ( isset( $wp_query->query_vars['doc_category'] ) ) {
263 $_args['tax_query'][] = [
264 'taxonomy' => 'doc_category',
265 'field' => 'slug',
266 'terms' => $wp_query->query_vars['doc_category'],
267 'operator' => 'AND',
268 'include_children' => false
269 ];
270 }
271
272 $_args['orderby'] = $_orderby;
273 if ( $_orderby != 'betterdocs_order' ) {
274 $_args['order'] = $_order;
275 }
276
277 /**
278 * Before Query
279 */
280 do_action_ref_array( 'betterdocs_navigation_docs_query', [ &$_args] );
281 $docs = $this->get_posts( $this->docs_query_args( $_args ) );
282
283 $_docs = [];
284 if ( $docs->have_posts() ) {
285 array_map( function ( $post ) use ( &$_docs ) {
286 $_docs[] = $post->ID;
287 }, $docs->posts );
288 }
289
290 $_docs_order = $_docs;
291 }
292
293 $_docs_order = apply_filters( 'betterdocs_adjacent_docs_order', $_docs_order, $_terms );
294
295 $_id_index = array_search( $post->ID, $_docs_order );
296 $_id_index = $adjacent === 'next' ? $_id_index + 1 : $_id_index - 1;
297 $_id = isset( $_docs_order[$_id_index] ) ? (int) $_docs_order[$_id_index] : null;
298
299 return $wpdb->prepare( "WHERE p.ID = %s AND $_where", (int) $_id );
300 }
301
302 public function parse_terms_args( $args = [] ) {
303 $_default_args = [
304 'hide_empty' => true,
305 'taxonomy' => 'doc_category'
306 ];
307
308 // OrderBy & Order
309 $_orderby = ! empty( $args['orderby'] ) && $args['orderby'] != 1 ? $args['orderby'] : 'name';
310 $_order = ! empty( $args['order'] ) ? $args['order'] : '';
311
312 if ( $_orderby == 'betterdocs_order' ) {
313 $args['meta_key'] = 'doc_category_order';
314 $args['orderby'] = 'meta_value_num';
315 $args['order'] = 'ASC';
316 } else {
317 $args['orderby'] = $_orderby;
318 $args['order'] = $_order;
319 }
320
321 // Nested Sub Category
322 // $args['parent'] = 0;
323 // if ( $nested_subcategory == true ) {
324 // }
325
326 if ( ! isset( $args['number'] ) ) {
327 global $wp_query;
328 if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
329 $args['number'] = 4;
330 }
331 }
332
333 // Includes
334 if ( isset( $args['include'] ) ) {
335 $_include = ! is_array( $args['include'] ) ? explode( ',', $args['include'] ) : $args['include'];
336 $args['include'] = $_include;
337 $args['orderby'] = 'include';
338
339 unset( $args['parent'] );
340 }
341
342 $_meta_query = ! empty( $args['meta_query'] ) ? $args['meta_query'] : [];
343 $args['meta_query'] = apply_filters( 'betterdocs_taxonomy_object_meta_query', $_meta_query, $args );
344
345 return apply_filters( 'betterdocs_category_terms_object', wp_parse_args( $args, $_default_args ), $args );
346 }
347
348 public function get_terms( $args ) {
349 return get_terms( $this->parse_terms_args( $args ) );
350 }
351
352 public function get_child_terms( $args ) {
353 if ( ! isset( $args['number'] ) ) {
354 global $wp_query;
355 if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
356 $args['number'] = 1;
357 }
358 }
359
360 return $this->get_terms( $args );
361 }
362
363 /**
364 * Get POSTs of Docs type.
365 *
366 * @param mixed $args
367 * @return WP_Query
368 */
369 public function get_posts( $args, $ignore = false ) {
370 if ( ! $ignore ) {
371 $args = $this->docs_query_args( $args );
372 }
373
374 return new WP_Query( $args );
375 }
376
377 public function get_taxonomy( $tax = '' ) {
378 global $wp_query;
379 if ( is_tax( 'knowledge_base' ) ) {
380 $_tax = $wp_query->tax_query->queried_terms;
381 if ( array_key_exists( "doc_category", $_tax ) ) {
382 $tax = 'doc_category';
383 } else {
384 $tax = 'knowledge_base';
385 }
386 } elseif ( is_tax( 'doc_category' ) ) {
387 $tax = 'doc_category';
388 }
389
390 return $tax;
391 }
392
393 public function terms_query( $args = [] ) {
394 global $wp_query;
395 $_origin_args = $args;
396
397 $default_args = [
398 'hide_empty' => true,
399 'taxonomy' => 'doc_category',
400 'orderby' => 'name'
401 ];
402
403 /**
404 * Number Set
405 *
406 * @FIX: If Built-in Docs page off and docs_page in use, then Terms Query Number 4|1 set.
407 */
408 // if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
409 // $default_args['number'] = 1;
410 // }
411
412 /**
413 * Nested Sub Category
414 */
415 if ( isset( $args['nested_subcategory'] ) && $args['nested_subcategory'] == true ) {
416 $default_args['parent'] = 0;
417 unset( $args['nested_subcategory'] );
418 // if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
419 // $default_args['number'] = 4;
420 // }
421 }
422
423 /**
424 * OrderBy and Order
425 */
426 if ( ! isset( $args['orderby'] ) ) {
427 $_orderby = $this->settings->get( 'terms_orderby', 'name' );
428 $_order = $this->settings->get( 'terms_order', '' );
429 } else {
430 $_orderby = $args['orderby'];
431 $_order = ! empty( $args['order'] ) ? $args['order'] : '';
432 }
433
434 if ( 'betterdocs_order' === $_orderby ) {
435 $default_args['meta_key'] = 'doc_category_order';
436 $_orderby = 'meta_value_num';
437 $_order = 'ASC';
438 } elseif ( $_orderby === true ) {
439 $_orderby = 'name';
440 }
441
442 $args['orderby'] = $_orderby;
443 if( ! empty( $_order ) ) {
444 $args['order'] = $_order;
445 }
446
447 /**
448 * @todo old hook
449 * hook: betterdocs_child_taxonomy_meta_query
450 */
451 $_multiple_kb = apply_filters(
452 'betterdocs_query_args_multiple_kb_enabled',
453 isset( $args['multiple_kb'] ) ? (bool) $args['multiple_kb'] : false,
454 $_origin_args
455 );
456
457 $_kb_slug = isset( $args['kb_slug'] ) ? trim( $args['kb_slug'] ) : '';
458
459 unset( $args['multiple_kb'] );
460 unset( $args['kb_slug'] );
461
462 $meta_query = ! empty( $args['meta_query'] ) ? $args['meta_query'] : [];
463 $meta_query = apply_filters( 'betterdocs_terms_meta_query_args', $meta_query, $_multiple_kb, $_kb_slug, $_origin_args );
464
465 if ( ! empty( $meta_query ) ) {
466 $default_args['meta_query'] = $meta_query;
467 }
468
469 if ( ! empty( $args['terms'] ) ) {
470 $args['include'] = explode( ',', $args['terms'] );
471 $args['orderby'] = 'include';
472 $args['order'] = 'ASC';
473
474 unset( $default_args['parent'] );
475 unset( $args['parent'] );
476 unset( $args['terms'] );
477 }
478
479 $_query_args = wp_parse_args( $args, $default_args );
480 return apply_filters( 'betterdocs_terms_query_args', $_query_args, $_origin_args );
481 }
482
483 public function get_term_parents( $term_id, $taxonomy = 'doc_category', $args = [] ) {
484 $term = get_term( $term_id, $taxonomy );
485 if ( is_wp_error( $term ) ) {
486 return $term;
487 }
488
489 if ( ! $term ) {
490 return [];
491 }
492
493 $_lists = [];
494 $origin_term = $term_id;
495 $term_id = $term->term_id;
496
497 $defaults = [
498 'format' => 'name',
499 'inclusive' => true
500 ];
501
502 $args = wp_parse_args( $args, $defaults );
503
504 $args['inclusive'] = wp_validate_boolean( $args['inclusive'] );
505
506 $parents = get_ancestors( $term_id, $taxonomy );
507
508 if ( $args['inclusive'] ) {
509 array_unshift( $parents, $term_id );
510 }
511
512 foreach ( array_reverse( $parents ) as $term_id ) {
513 $parent = get_term( $term_id, $taxonomy );
514 $name = ( 'slug' === $args['format'] ) ? $parent->slug : $parent->name;
515 $term_permalink = get_term_link( $parent->term_id, $taxonomy );
516 $term_permalink = apply_filters( 'betterdocs_breadcrumb_term_permalink', $term_permalink, $term_id );
517
518 $_item = [
519 'url' => $term_permalink,
520 'text' => $name
521 ];
522
523 $_lists[] = $_item;
524 }
525
526 return apply_filters( 'betterdocs_breadcrumb_archive_lists', $_lists, $origin_term );
527 }
528
529 /**
530 * Summary of docs_query_args
531 * @param mixed $args
532 * @throws \Exception
533 * @return mixed
534 */
535 public function docs_query_args( $args, $filter = [] ) {
536 $_origin_args = $args;
537
538 $default_args = [
539 'post_type' => 'docs'
540 ];
541
542 if ( ! empty( $args['post_type'] ) && trim( $args['post_type'] ) === 'docs_any' ) {
543 $default_args['post_type'] = 'docs';
544 $default_args['post_status'] = 'any';
545
546 unset( $args['post_type'] );
547 }
548
549 /**
550 * OrderBy and Order
551 */
552 if ( ! isset( $args['orderby'] ) ) {
553 $_orderby = $this->settings->get( 'alphabetically_order_post' );
554 $_order = $this->settings->get( 'docs_order', 'ASC' );
555 } else {
556 $_orderby = $args['orderby'];
557 $_order = ! empty( $args['order'] ) ? $args['order'] : 'ASC';
558 }
559
560 if ( 'betterdocs_order' != $_orderby ) {
561 if ( $_orderby === true ) {
562 $args['orderby'] = 'title';
563 } else {
564 $args['orderby'] = $_orderby;
565 }
566
567 $args['order'] = $_order;
568 } elseif ( 'betterdocs_order' == $_orderby ) {
569 unset( $args['orderby'] );
570 }
571
572 if ( empty( $args['orderby'] ) ) {
573 unset( $args['order'] );
574 }
575
576 /**
577 * Term ID
578 */
579 $_term_id = null;
580 if ( ! empty( $args['term_id'] ) ) {
581 $_term_id = intval( $args['term_id'] );
582 unset( $args['term_id'] );
583 }
584
585 // if ( $_term_id == null ) {
586 // throw new \Exception( __( '$args["term_id"] cannot be null.', 'betterdocs' ) );
587 // }
588
589 /**
590 * Term Slug for tax_query
591 */
592 $_term_slug = '';
593 if ( ! empty( $args['term_slug'] ) ) {
594 $_term_slug = trim( $args['term_slug'] );
595 unset( $args['term_slug'] );
596 }
597
598 /**
599 * @todo old hook
600 * hook: betterdocs_cat_template_multikb
601 */
602
603 $_multiple_kb = apply_filters(
604 'betterdocs_enable_multiple_knowledge_base',
605 isset( $args['multiple_kb'] ) ? (bool) $args['multiple_kb'] : false,
606 $_origin_args
607 );
608
609 $_kb_slug = isset( $args['kb_slug'] ) ? trim( $args['kb_slug'] ) : '';
610
611 unset( $args['multiple_kb'] );
612 unset( $args['kb_slug'] );
613
614 $tax_query = [
615 [
616 'taxonomy' => 'doc_category',
617 'field' => 'slug',
618 'terms' => $_term_slug,
619 'operator' => 'AND',
620 'include_children' => false
621 ]
622 ];
623
624 if ( isset( $args['tax_query'] ) ) {
625 $tax_query = $args['tax_query'];
626 }
627
628 $args['tax_query'] = apply_filters(
629 'betterdocs_docs_tax_query_args',
630 $tax_query, $_multiple_kb, $_term_slug, $_kb_slug, $_origin_args
631 );
632 /**
633 * Final parse args
634 */
635 $args = wp_parse_args( $args, $default_args );
636
637 if ( ! empty( $filter ) ) {
638 $filter = array_flip( $filter );
639 $args = array_filter( $args, function ( $item ) use ( $filter ) {
640 return ! array_key_exists( $item, $filter );
641 }, ARRAY_FILTER_USE_KEY );
642 }
643
644 return apply_filters( 'betterdocs_articles_args', $args, $_term_id, $_origin_args );
645 }
646
647 public function faq_terms_query_args( $includes = '', $excludes = '', $args = [] ) {
648 $_args = [
649 'taxonomy' => 'betterdocs_faq_category',
650 'meta_key' => 'order',
651 'orderby' => 'meta_value_num',
652 'order' => 'ASC',
653 'include' => $includes,
654 'exclude' => $excludes,
655 'meta_query' => [
656 [
657 'key' => 'status',
658 'value' => 1,
659 'compare' => '=='
660 ]
661 ]
662 ];
663
664 if ( $_args['include'] == 'all' ) {
665 unset( $_args['include'] );
666 unset( $_args['exclude'] );
667 }
668
669 if ( empty( $_args['exclude'] ) ) {
670 unset( $_args['exclude'] );
671 }
672
673 if ( empty( $_args['include'] ) ) {
674 unset( $_args['include'] );
675 }
676
677 return wp_parse_args( $args, $_args );
678 }
679
680 public function get_faq_by_term( $term_id ) {
681 global $wpdb;
682
683 $args = [
684 'post_type' => 'betterdocs_faq',
685 'post_status' => 'publish',
686 'tax_query' => [
687 [
688 'taxonomy' => 'betterdocs_faq_category',
689 'field' => 'term_id',
690 'terms' => $term_id,
691 'operator' => 'AND'
692 ]
693 ],
694 'posts_per_page' => -1
695 ];
696
697 $args['orderby'] = 'post__in';
698 $args['post__in'] = $this->get_faq_orders( $term_id );
699
700 return new WP_Query( $args );
701 }
702
703 public function get_faq_orders( $term_id = null ) {
704 global $wpdb;
705 $faq_order = get_term_meta( $term_id, '_betterdocs_faq_order', true );
706 $faq_order = explode( ',', $faq_order );
707
708 $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = %d", $term_id );
709 $query_key = "betterdocs_faq_order_" . md5( $query );
710
711 if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
712 return $results;
713 }
714
715 if ( ! empty( $faq_order ) ) {
716 $new_ids = [];
717 $results = $wpdb->get_results( $query );
718
719 if ( ! is_null( $results ) && ! empty( $results ) && is_array( $results ) ) {
720 $object_ids = array_filter( $results, function ( $value ) use ( $faq_order ) {
721 return ! in_array( $value->object_id, $faq_order );
722 } );
723
724 if ( ! empty( $object_ids ) ) {
725 array_walk( $object_ids, function ( $value ) use ( &$new_ids ) {
726 $new_ids[] = $value->object_id;
727 } );
728 }
729 }
730
731 $faq_order = array_merge( $new_ids, $faq_order );
732 }
733
734 $this->database->set_cache( $query_key, $faq_order, 1 );
735
736 return $faq_order;
737 }
738
739 public function get_faq_terms( $terms = [] ) {
740 $_terms = get_terms( [
741 'taxonomy' => 'betterdocs_faq_category',
742 'hide_empty' => true,
743 'orderby' => 'name',
744 'order' => 'ASC',
745 'meta_query' => [
746 [
747 'key' => 'status',
748 'value' => 1,
749 'compare' => '=='
750 ]
751 ]
752 ] );
753
754 if ( ! is_wp_error( $_terms ) ) {
755 foreach ( $_terms as $term ) {
756 $terms[$term->term_id] = $term->name;
757 }
758 }
759
760 return $terms;
761 }
762
763 public function get_docs_count( $term, $nested_subcategory = false, $args = [] ) {
764 $counts = isset( $term->count ) ? $term->count : 0;
765
766 if ( $nested_subcategory == false ) {
767 return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
768 }
769
770 $_child_terms_docs_ids = $this->get_doc_ids_by_term( $term, null, $nested_subcategory );
771 if ( is_array( $_child_terms_docs_ids ) ) {
772 $counts = count( $_child_terms_docs_ids );
773 }
774
775 return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
776 }
777
778 public function get_doc_ids_by_term( $term, $optional = null, $nested_subcategory = false ) {
779 $args = [ 'include' => $term->term_id ];
780 if( $nested_subcategory ) {
781 $args['child_of'] = $term->term_id;
782 unset( $args['include'] );
783 }
784 $_child_terms = get_terms( $term->taxonomy, $args );
785
786 if ( ! is_array( $_child_terms ) ) {
787 return false;
788 }
789
790 array_unshift( $_child_terms, $term );
791
792 $_child_terms_ids = array_column( $_child_terms, 'term_id' );
793 $_child_terms_taxs = array_column( $_child_terms, 'taxonomy' );
794 $_child_terms_docs_ids = get_objects_in_term( $_child_terms_ids, $_child_terms_taxs );
795
796 if ( $optional !== null ) {
797 $_optional_doc_ids = get_objects_in_term( $optional->term_id, $optional->taxonomy );
798 $_child_terms_docs_ids = array_intersect( $_child_terms_docs_ids, $_optional_doc_ids );
799 }
800
801 return array_filter( $_child_terms_docs_ids, function ( $doc_id ) {
802 if ( ! is_user_logged_in() ) {
803 return is_post_publicly_viewable( $doc_id );
804 }
805
806 $_status = get_post_status( $doc_id );
807 return $_status == 'private' || is_post_publicly_viewable( $doc_id );
808 } );
809 }
810 }
811