PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.9
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/Core/Query.php +1035 -640 3.5.23.8.9 View file →
@@ -7,787 +7,949 @@
7 7 use WPDeveloper\BetterDocs\Utils\Database;
8 8 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
9 9
10 10 class Query extends Base {
11 - private $container;
12 - protected $database;
13 - protected $settings;
11 + private $container;
12 + protected $database;
13 + protected $settings;
14 14
15 - public function __construct( Container $container, Database $database, Settings $settings ) {
16 - $this->container = $container;
17 - $this->database = $database;
18 - $this->settings = $settings;
15 + public function __construct( Container $container, Database $database, Settings $settings ) {
16 + $this->container = $container;
17 + $this->database = $database;
18 + $this->settings = $settings;
19 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 );
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 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 );
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 38
39 - $this->init();
39 + $this->init();
40 40
41 - /**
42 - * Modify Popular Docs Query (For Shortcode & Widget)
43 - */
44 - add_filter( 'posts_clauses', [$this, 'mod_query_popular_docs'], 10, 2 );
45 - }
41 + /**
42 + * Modify Popular Docs Query (For Shortcode & Widget)
43 + */
44 + add_filter( 'posts_clauses', [ $this, 'mod_query_popular_docs' ], 10, 2 );
45 + }
46 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 - }
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'] = ! is_user_logged_in() ? "AND ( ( {$wpdb->prefix}posts.post_type = 'docs' ) AND ( {$wpdb->prefix}posts.post_status = 'publish' OR {$wpdb->prefix}posts.post_status = 'future' ) )" : "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 + if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ) {
58 + global $sitepress;
59 + if ( $sitepress->is_setup_complete() ) {
60 + $constant_language_code = ICL_LANGUAGE_CODE;
61 + $clauses['join'] .= " JOIN {$wpdb->prefix}icl_translations ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}icl_translations.element_id";
62 + $clauses['where'] .= " AND ( {$wpdb->prefix}icl_translations.language_code = '{$constant_language_code}' ) AND ( {$wpdb->prefix}icl_translations.element_type = CONCAT('post_', {$wpdb->prefix}posts.post_type ) )";
63 + }
64 + }
65 + }
66 + }
67 + return $clauses;
68 + }
61 69
62 - public function init() {
63 - }
70 + public function init() {
71 + }
64 72
65 - public function parse_term_query( $term_query ) {
66 - if ( empty( $term_query->query_vars['taxonomy'] ) ) {
67 - return;
68 - }
73 + public function parse_term_query( $term_query ) {
74 + if ( empty( $term_query->query_vars['taxonomy'] ) ) {
75 + return;
76 + }
69 77
70 - if ( ! in_array( 'doc_category', $term_query->query_vars['taxonomy'], true ) ) {
71 - return;
72 - }
78 + if ( ! in_array( 'doc_category', $term_query->query_vars['taxonomy'], true ) ) {
79 + return;
80 + }
73 81
74 - global $current_screen;
82 + global $current_screen;
75 83
76 - if ( $current_screen == null ) {
77 - return;
78 - }
84 + if ( $current_screen == null ) {
85 + return;
86 + }
79 87
80 - if ( $current_screen->taxonomy !== 'doc_category' || $current_screen->id != 'edit-doc_category' ) {
81 - return;
82 - }
88 + if ( $current_screen->taxonomy !== 'doc_category' || $current_screen->id != 'edit-doc_category' ) {
89 + return;
90 + }
83 91
84 - $term_query->query_vars['meta_query'] = [[
85 - 'key' => 'doc_category_order',
86 - 'type' => 'NUMERIC'
87 - ]];
92 + $term_query->query_vars['meta_query'] = [
93 + [
94 + 'key' => 'doc_category_order',
95 + 'type' => 'NUMERIC'
96 + ]
97 + ];
88 98
89 - $term_query->query_vars['orderby'] = 'meta_value_num';
90 - }
99 + $term_query->query_vars['orderby'] = 'meta_value_num';
100 + }
91 101
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 - }
102 + public function parse_query( &$query ) {
103 + // dump( is_single(), $query->query_vars );
104 + // if ( is_single() && isset( $query->query_vars['post_type'] ) && $query->query_vars['post_type'] == 'docs' ) {
105 + // $query->is_single = false;
106 + // $query->is_archive = true;
107 + // $query->set( 'knowledge_base', $query->query_vars['docs'] );
108 + // }
109 + }
100 110
101 - public function pre_get_posts( &$query ) {
102 - if ( is_admin() || ! $query->is_main_query() ) {
103 - return;
104 - }
111 + public function pre_get_posts( &$query ) {
112 + if ( is_admin() || ! $query->is_main_query() ) {
113 + return;
114 + }
105 115
106 - if ( is_tax( 'doc_category' ) ) {
107 - $query->set( 'post_type', 'docs' );
108 - $query->set( 'posts_per_archive_page', -1 );
116 + if ( is_tax( 'doc_category' ) ) {
117 + $query->set( 'post_type', 'docs' );
118 + $query->set( 'posts_per_archive_page', -1 );
109 119
110 - $term = get_term_by( 'slug', $query->get( 'doc_category', '' ), 'doc_category' );
120 + $term = get_term_by( 'slug', $query->get( 'doc_category', '' ), 'doc_category' );
111 121
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 - }
122 + $post__in = $this->get_docs_order_by_terms( $term->term_id );
123 + if ( ! empty( $post__in ) ) {
124 + $query->set( 'orderby', 'post__in' );
125 + $query->set( 'post__in', $post__in );
126 + }
117 127
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 - }
128 + // if ( ! empty( $query->query_vars['knowledge_base'] ) ) {
129 + // $query->betterdocs_terms = get_terms( [
130 + // 'taxonomy' => 'doc_category',
131 + // 'parent' => 0,
132 + // 'hide_empty' => true,
133 + // 'meta_query' => [
134 + // 'relation' => 'OR',
135 + // [
136 + // 'key' => 'doc_category_knowledge_base',
137 + // 'value' => $query->query_vars['knowledge_base'],
138 + // 'compare' => 'LIKE'
139 + // ]
140 + // ]
141 + // ] );
142 + // }
143 + }
134 144
135 - // dump( is_archive(), $query->query_vars );
136 - }
145 + // dump( is_archive(), $query->query_vars );
146 + }
137 147
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 );
148 + /**
149 + * Get docs orders by term_id.
150 + *
151 + * @since 2.5.0
152 + * @param int $term_id
153 + *
154 + * @return array
155 + */
156 + public function get_docs_order_by_terms( $term_id ) {
157 + global $wpdb;
158 + $_docs_order = get_term_meta( $term_id, '_docs_order', true );
159 + $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = %d", $term_id );
160 + $query_key = "docs_order_by_terms_{$term_id}_" . md5( $query );
151 161
152 - if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
153 - return $results;
154 - }
162 + if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
163 + return $results;
164 + }
155 165
156 - if ( ! empty( $_docs_order ) ) {
157 - $_docs_order = explode( ',', $_docs_order );
158 - $new_ids = [];
166 + if ( ! empty( $_docs_order ) ) {
167 + $_docs_order = explode( ',', $_docs_order );
168 + $new_ids = [];
159 169
160 - $results = $wpdb->get_results( $query );
170 + $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
161 171
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 - } );
172 + if ( is_array( $results ) && ! empty( $results ) ) {
173 + $object_ids = array_filter(
174 + $results,
175 + function ( $value ) use ( $_docs_order ) {
176 + return ! in_array( $value->object_id, $_docs_order );
177 + }
178 + );
166 179
167 - if ( ! empty( $object_ids ) ) {
168 - array_walk( $object_ids, function ( $value ) use ( &$new_ids ) {
169 - $new_ids[] = $value->object_id;
170 - } );
171 - }
172 - }
180 + if ( ! empty( $object_ids ) ) {
181 + array_walk(
182 + $object_ids,
183 + function ( $value ) use ( &$new_ids ) {
184 + $new_ids[] = $value->object_id;
185 + }
186 + );
187 + }
188 + }
173 189
174 - $_docs_order = array_merge( $new_ids, $_docs_order );
175 - $this->database->set_cache( $query_key, $_docs_order, 1 );
190 + $_docs_order = array_merge( $new_ids, $_docs_order );
191 + $this->database->set_cache( $query_key, $_docs_order, 1 );
176 192
177 - return $_docs_order;
178 - }
193 + return $_docs_order;
194 + }
179 195
180 - return [];
181 - }
196 + return [];
197 + }
182 198
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 - }
199 + /**
200 + * For determine the next 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 next_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
212 + return $this->get_adjacent_post_id( 'next', $where, $post, $taxonomy );
213 + }
198 214
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 - }
215 + /**
216 + * For determine the previous post ID
217 + *
218 + * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
219 + *
220 + * @param mixed $where
221 + * @param mixed $in_same_term
222 + * @param mixed $excluded_terms
223 + * @param mixed $taxonomy
224 + * @param mixed $post
225 + * @return mixed
226 + */
227 + public function previous_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
228 + return $this->get_adjacent_post_id( 'previous', $where, $post, $taxonomy );
229 + }
214 230
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 - }
231 + /**
232 + * Get where clause for next/previous post ID mainly used in post navigation.
233 + *
234 + * @see `previous_post_where` and `next_post_where` methods.
235 + *
236 + * @param mixed $adjacent
237 + * @param mixed $where
238 + * @param mixed $post
239 + * @param mixed $taxonomy
240 + * @return mixed
241 + */
242 + private function get_adjacent_post_id( $adjacent, $where, $post, $taxonomy ) {
243 + if ( $taxonomy !== 'doc_category' ) {
244 + return $where;
245 + }
230 246
231 - $_id = null;
232 - $_terms = get_the_terms( $post->ID, 'doc_category' );
233 - if ( empty( $_terms ) ) {
234 - return $where;
235 - }
247 + $_id = null;
248 + $_terms = get_the_terms( $post->ID, 'doc_category' );
249 + if ( empty( $_terms ) ) {
250 + return $where;
251 + }
236 252
237 - global $wp_query, $wpdb;
253 + global $wp_query, $wpdb;
238 254
239 - $_docs_order = $this->get_docs_order_by_terms( $_terms[0]->term_id );
255 + $_docs_order = $this->get_docs_order_by_terms( $_terms[0]->term_id );
240 256
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 - }
257 + $_where = explode( 'AND', $where );
258 + if ( is_array( $_where ) ) {
259 + // $_where[0] = str_replace( 'WHERE ', '', $_where[0] );
260 + unset( $_where[0] );
261 + $_where = implode( 'AND', $_where );
262 + }
247 263
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 : [];
264 + $_orderby = $this->settings->get( 'alphabetically_order_post', 'betterdocs_order' );
265 + $_order = $this->settings->get( 'docs_order', 'ASC' );
266 + $_docs_order = $_orderby === 'betterdocs_order' ? $_docs_order : [];
251 267
252 - if ( empty( $_docs_order ) ) {
253 - $statuses = [ 'publish' ];
268 + if ( empty( $_docs_order ) ) {
269 + $statuses = [ 'publish' ];
254 270
255 - if( is_user_logged_in() ) {
256 - $statuses[] = 'private';
257 - }
271 + if ( is_user_logged_in() ) {
272 + $statuses[] = 'private';
273 + }
258 274
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 - }
275 + $_args = [
276 + 'post_status' => $statuses
277 + ];
278 + if ( isset( $wp_query->query_vars['doc_category'] ) ) {
279 + $_args['tax_query'][] = [
280 + 'taxonomy' => 'doc_category',
281 + 'field' => 'slug',
282 + 'terms' => $wp_query->query_vars['doc_category'],
283 + 'operator' => 'AND',
284 + 'include_children' => false
285 + ];
286 + }
271 287
272 - $_args['orderby'] = $_orderby;
273 - if ( $_orderby != 'betterdocs_order' ) {
274 - $_args['order'] = $_order;
275 - }
288 + $_args['orderby'] = $_orderby;
289 + if ( $_orderby != 'betterdocs_order' ) {
290 + $_args['order'] = $_order;
291 + }
276 292
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 ) );
293 + /**
294 + * Before Query
295 + */
296 + do_action_ref_array( 'betterdocs_navigation_docs_query', [ &$_args ] );
297 + $docs = $this->get_posts( $this->docs_query_args( $_args ) );
282 298
283 - $_docs = [];
284 - if ( $docs->have_posts() ) {
285 - array_map( function ( $post ) use ( &$_docs ) {
286 - $_docs[] = $post->ID;
287 - }, $docs->posts );
288 - }
299 + $_docs = [];
300 + if ( $docs->have_posts() ) {
301 + array_map(
302 + function ( $post ) use ( &$_docs ) {
303 + $_docs[] = $post->ID;
304 + },
305 + $docs->posts
306 + );
307 + }
289 308
290 - $_docs_order = $_docs;
291 - }
309 + $_docs_order = $_docs;
310 + }
292 311
293 - $_docs_order = apply_filters( 'betterdocs_adjacent_docs_order', $_docs_order, $_terms );
312 + $_docs_order = apply_filters( 'betterdocs_adjacent_docs_order', $_docs_order, $_terms );
294 313
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;
314 + $_id_index = array_search( $post->ID, $_docs_order );
315 + $_id_index = $adjacent === 'next' ? $_id_index + 1 : $_id_index - 1;
316 + $_id = isset( $_docs_order[ $_id_index ] ) ? (int) $_docs_order[ $_id_index ] : null;
298 317
299 - return $wpdb->prepare( "WHERE p.ID = %s AND $_where", (int) $_id );
300 - }
318 + return $wpdb->prepare( "WHERE p.ID = %s AND $_where", (int) $_id );
319 + }
301 320
302 - public function parse_terms_args( $args = [] ) {
303 - $_default_args = [
304 - 'hide_empty' => true,
305 - 'taxonomy' => 'doc_category'
306 - ];
321 + public function parse_terms_args( $args = [] ) {
322 + $_default_args = [
323 + 'hide_empty' => true,
324 + 'taxonomy' => 'doc_category'
325 + ];
307 326
308 - // OrderBy & Order
309 - $_orderby = ! empty( $args['orderby'] ) && $args['orderby'] != 1 ? $args['orderby'] : 'name';
310 - $_order = ! empty( $args['order'] ) ? $args['order'] : '';
327 + // OrderBy & Order
328 + $_orderby = ! empty( $args['orderby'] ) && $args['orderby'] != 1 ? $args['orderby'] : 'name';
329 + $_order = ! empty( $args['order'] ) ? $args['order'] : '';
311 330
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 - }
331 + if ( $_orderby == 'betterdocs_order' ) {
332 + $args['meta_key'] = 'doc_category_order';
333 + $args['orderby'] = 'meta_value_num';
334 + $args['order'] = 'ASC';
335 + } else {
336 + $args['orderby'] = $_orderby;
337 + $args['order'] = $_order;
338 + }
320 339
321 - // Nested Sub Category
322 - // $args['parent'] = 0;
323 - // if ( $nested_subcategory == true ) {
324 - // }
340 + // Nested Sub Category
341 + // $args['parent'] = 0;
342 + // if ( $nested_subcategory == true ) {
343 + // }
325 344
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 - }
345 + if ( ! isset( $args['number'] ) ) {
346 + global $wp_query;
347 + if ( $wp_query->query === null || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
348 + $args['number'] = 4;
349 + }
350 + }
332 351
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';
352 + // Includes
353 + if ( isset( $args['include'] ) ) {
354 + $_include = ! is_array( $args['include'] ) ? explode( ',', $args['include'] ) : $args['include'];
355 + $args['include'] = $_include;
356 + $args['orderby'] = 'include';
338 357
339 - unset( $args['parent'] );
340 - }
358 + unset( $args['parent'] );
359 + }
341 360
342 - $_meta_query = ! empty( $args['meta_query'] ) ? $args['meta_query'] : [];
343 - $args['meta_query'] = apply_filters( 'betterdocs_taxonomy_object_meta_query', $_meta_query, $args );
361 + $_meta_query = ! empty( $args['meta_query'] ) ? $args['meta_query'] : [];
362 + $args['meta_query'] = apply_filters( 'betterdocs_taxonomy_object_meta_query', $_meta_query, $args );
344 363
345 - return apply_filters( 'betterdocs_category_terms_object', wp_parse_args( $args, $_default_args ), $args );
346 - }
364 + return apply_filters( 'betterdocs_category_terms_object', wp_parse_args( $args, $_default_args ), $args );
365 + }
347 366
348 - public function get_terms( $args ) {
349 - return get_terms( $this->parse_terms_args( $args ) );
350 - }
367 + public function get_terms( $args ) {
368 + return get_terms( $this->parse_terms_args( $args ) );
369 + }
351 370
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 - }
371 + public function get_child_terms( $args ) {
372 + if ( ! isset( $args['number'] ) ) {
373 + global $wp_query;
374 + if ( $wp_query->query === null || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
375 + $args['number'] = 1;
376 + }
377 + }
359 378
360 - return $this->get_terms( $args );
361 - }
379 + return $this->get_terms( $args );
380 + }
362 381
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 - }
382 + /**
383 + * Get POSTs of Docs type.
384 + *
385 + * @param mixed $args
386 + * @return WP_Query
387 + */
388 + public function get_posts( $args, $ignore = false ) {
389 + if ( ! $ignore ) {
390 + $args = $this->docs_query_args( $args );
391 + }
373 392
374 - return new WP_Query( $args );
375 - }
393 + return new WP_Query( $args );
394 + }
376 395
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 - }
396 + public function get_taxonomy( $tax = '' ) {
397 + global $wp_query;
398 + if ( is_tax( 'knowledge_base' ) ) {
399 + $_tax = $wp_query->tax_query->queried_terms;
400 + if ( array_key_exists( 'doc_category', $_tax ) ) {
401 + $tax = 'doc_category';
402 + } else {
403 + $tax = 'knowledge_base';
404 + }
405 + } elseif ( is_tax( 'doc_category' ) ) {
406 + $tax = 'doc_category';
407 + }
389 408
390 - return $tax;
391 - }
409 + return $tax;
410 + }
392 411
393 - public function terms_query( $args = [] ) {
394 - global $wp_query;
395 - $_origin_args = $args;
412 + public function terms_query( $args = [] ) {
413 + global $wp_query;
414 + $_origin_args = $args;
396 415
397 - $default_args = [
398 - 'hide_empty' => true,
399 - 'taxonomy' => 'doc_category',
400 - 'orderby' => 'name'
401 - ];
416 + $default_args = [
417 + 'hide_empty' => true,
418 + 'taxonomy' => 'doc_category',
419 + 'orderby' => 'name'
420 + ];
402 421
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 - // }
422 + /**
423 + * Number Set
424 + *
425 + * @FIX: If Built-in Docs page off and docs_page in use, then Terms Query Number 4|1 set.
426 + */
427 + // if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
428 + // $default_args['number'] = 1;
429 + // }
411 430
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 - }
431 + /**
432 + * Nested Sub Category
433 + */
434 + if ( isset( $args['nested_subcategory'] ) && $args['nested_subcategory'] == true ) {
435 + $default_args['parent'] = 0;
436 + unset( $args['nested_subcategory'] );
437 + // if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
438 + // $default_args['number'] = 4;
439 + // }
440 + }
422 441
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 - }
442 + /**
443 + * OrderBy and Order
444 + */
445 + if ( ! isset( $args['orderby'] ) ) {
446 + $_orderby = $this->settings->get( 'terms_orderby', 'name' );
447 + $_order = $this->settings->get( 'terms_order', '' );
448 + } else {
449 + $_orderby = $args['orderby'];
450 + $_order = ! empty( $args['order'] ) ? $args['order'] : '';
451 + }
433 452
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 - }
453 + if ( 'betterdocs_order' === $_orderby ) {
454 + $default_args['meta_key'] = 'doc_category_order';
455 + $_orderby = 'meta_value_num';
456 + $_order = 'ASC';
457 + } elseif ( $_orderby === true ) {
458 + $_orderby = 'name';
459 + }
441 460
442 - $args['orderby'] = $_orderby;
443 - if( ! empty( $_order ) ) {
444 - $args['order'] = $_order;
445 - }
461 + $args['orderby'] = $_orderby;
462 + if ( ! empty( $_order ) ) {
463 + $args['order'] = $_order;
464 + }
446 465
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 - );
466 + /**
467 + * @todo old hook
468 + * hook: betterdocs_child_taxonomy_meta_query
469 + */
470 + $_multiple_kb = apply_filters(
471 + 'betterdocs_query_args_multiple_kb_enabled',
472 + isset( $args['multiple_kb'] ) ? (bool) $args['multiple_kb'] : false,
473 + $_origin_args
474 + );
456 475
457 - $_kb_slug = isset( $args['kb_slug'] ) ? trim( $args['kb_slug'] ) : '';
476 + $_kb_slug = isset( $args['kb_slug'] ) ? trim( $args['kb_slug'] ) : '';
458 477
459 - unset( $args['multiple_kb'] );
460 - unset( $args['kb_slug'] );
478 + unset( $args['multiple_kb'] );
479 + unset( $args['kb_slug'] );
461 480
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 );
481 + $meta_query = ! empty( $args['meta_query'] ) ? $args['meta_query'] : [];
482 + $meta_query = apply_filters( 'betterdocs_terms_meta_query_args', $meta_query, $_multiple_kb, $_kb_slug, $_origin_args );
464 483
465 - if ( ! empty( $meta_query ) ) {
466 - $default_args['meta_query'] = $meta_query;
467 - }
484 + if ( ! empty( $meta_query ) ) {
485 + $default_args['meta_query'] = $meta_query;
486 + }
468 487
469 - if ( ! empty( $args['terms'] ) ) {
470 - $args['include'] = explode( ',', $args['terms'] );
471 - $args['orderby'] = 'include';
472 - $args['order'] = 'ASC';
488 + if ( ! empty( $args['terms'] ) ) {
489 + $args['include'] = explode( ',', $args['terms'] );
490 + $args['orderby'] = 'include';
491 + $args['order'] = 'ASC';
473 492
474 - unset( $default_args['parent'] );
475 - unset( $args['parent'] );
476 - unset( $args['terms'] );
477 - }
493 + unset( $default_args['parent'] );
494 + unset( $args['parent'] );
495 + unset( $args['terms'] );
496 + }
478 497
479 - $_query_args = wp_parse_args( $args, $default_args );
480 - return apply_filters( 'betterdocs_terms_query_args', $_query_args, $_origin_args );
481 - }
498 + $_query_args = wp_parse_args( $args, $default_args );
499 + return apply_filters( 'betterdocs_terms_query_args', $_query_args, $_origin_args );
500 + }
482 501
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 - }
502 + public function get_term_parents( $term_id, $taxonomy = 'doc_category', $args = [] ) {
503 + $term = get_term( $term_id, $taxonomy );
504 + if ( is_wp_error( $term ) ) {
505 + return $term;
506 + }
488 507
489 - if ( ! $term ) {
490 - return [];
491 - }
508 + if ( ! $term ) {
509 + return [];
510 + }
492 511
493 - $_lists = [];
494 - $origin_term = $term_id;
495 - $term_id = $term->term_id;
512 + $_lists = [];
513 + $origin_term = $term_id;
514 + $term_id = $term->term_id;
496 515
497 - $defaults = [
498 - 'format' => 'name',
499 - 'inclusive' => true
500 - ];
516 + $defaults = [
517 + 'format' => 'name',
518 + 'inclusive' => true
519 + ];
501 520
502 - $args = wp_parse_args( $args, $defaults );
521 + $args = wp_parse_args( $args, $defaults );
503 522
504 - $args['inclusive'] = wp_validate_boolean( $args['inclusive'] );
523 + $args['inclusive'] = wp_validate_boolean( $args['inclusive'] );
505 524
506 - $parents = get_ancestors( $term_id, $taxonomy );
525 + $parents = get_ancestors( $term_id, $taxonomy );
507 526
508 - if ( $args['inclusive'] ) {
509 - array_unshift( $parents, $term_id );
510 - }
527 + if ( $args['inclusive'] ) {
528 + array_unshift( $parents, $term_id );
529 + }
511 530
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 );
531 + foreach ( array_reverse( $parents ) as $term_id ) {
532 + $parent = get_term( $term_id, $taxonomy );
533 + $name = ( 'slug' === $args['format'] ) ? $parent->slug : $parent->name;
534 + $term_permalink = get_term_link( $parent->term_id, $taxonomy );
535 + $term_permalink = apply_filters( 'betterdocs_breadcrumb_term_permalink', $term_permalink, $term_id );
517 536
518 - $_item = [
519 - 'url' => $term_permalink,
520 - 'text' => $name
521 - ];
537 + $_item = [
538 + 'url' => $term_permalink,
539 + 'text' => $name
540 + ];
522 541
523 - $_lists[] = $_item;
524 - }
542 + $_lists[] = $_item;
543 + }
525 544
526 - return apply_filters( 'betterdocs_breadcrumb_archive_lists', $_lists, $origin_term );
527 - }
545 + return apply_filters( 'betterdocs_breadcrumb_archive_lists', $_lists, $origin_term );
546 + }
528 547
529 - public function is_inner_templates() {
530 - if ( is_tax( 'knowledge_base' ) || is_tax( 'doc_category' ) || is_tax( 'doc_tag' ) || is_singular( 'docs' ) ) {
531 - return true;
532 - }
533 - return false;
534 - }
548 + /**
549 + * Get all non-empty child term IDs recursively for a given taxonomy and parent term.
550 + *
551 + * This function retrieves all child terms for a specified taxonomy and parent term,
552 + * recursively fetching child terms of child terms, and returns only those with a non-zero post count.
553 + *
554 + * @param string $taxonomy The taxonomy name (e.g., 'doc_category').
555 + * @param int $parent_id The ID of the parent term to start retrieving children from.
556 + *
557 + * @return array An array of non-empty child term IDs.
558 + */
559 + public function get_all_child_term_ids( $taxonomy, $parent_id ) {
560 + // Set up the arguments for retrieving child terms
561 + $args = apply_filters(
562 + 'betterdocs_get_child_term_ids_args',
563 + [
564 + 'taxonomy' => $taxonomy,
565 + 'parent' => $parent_id,
566 + 'hide_empty' => true,
567 + 'fields' => 'ids'
568 + ]
569 + );
535 570
536 - /**
537 - * Summary of docs_query_args
538 - * @param mixed $args
539 - * @throws \Exception
540 - * @return mixed
541 - */
542 - public function docs_query_args( $args, $filter = [] ) {
543 - $_origin_args = $args;
571 + // Get the terms based on the arguments
572 + $terms = get_terms( $args );
544 573
545 - $default_args = [
546 - 'post_type' => 'docs'
547 - ];
574 + // Initialize an empty array to hold non-empty child term IDs
575 + $non_empty_children = [];
548 576
549 - if ( ! empty( $args['post_type'] ) && trim( $args['post_type'] ) === 'docs_any' ) {
550 - $default_args['post_type'] = 'docs';
551 - $default_args['post_status'] = 'any';
577 + // Check if terms were retrieved without errors and that the result isn't empty
578 + if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
579 + $term_ids = [];
552 580
553 - unset( $args['post_type'] );
554 - }
581 + // Loop through each term ID
582 + foreach ( $terms as $term_id ) {
583 + // Add the current term ID to the term_ids array
584 + $term_ids[] = $term_id;
555 585
556 - /**
557 - * OrderBy and Order
558 - */
559 - if ( ! isset( $args['orderby'] ) ) {
560 - $_orderby = $this->settings->get( 'alphabetically_order_post' );
561 - $_order = $this->settings->get( 'docs_order', 'ASC' );
562 - } else {
563 - $_orderby = $args['orderby'];
564 - $_order = ! empty( $args['order'] ) ? $args['order'] : 'ASC';
565 - }
586 + // Recursively get child terms for the current term
587 + $child_term_ids = $this->get_all_child_term_ids( $taxonomy, $term_id );
566 588
567 - if ( 'betterdocs_order' != $_orderby ) {
568 - if ( $_orderby === true ) {
569 - $args['orderby'] = 'title';
570 - } else {
571 - $args['orderby'] = $_orderby;
572 - }
589 + // If there are child terms, merge them into the term_ids array
590 + if ( ! empty( $child_term_ids ) ) {
591 + $term_ids = array_merge( $term_ids, $child_term_ids );
592 + }
593 + }
573 594
574 - $args['order'] = $_order;
575 - } elseif ( 'betterdocs_order' == $_orderby ) {
576 - unset( $args['orderby'] );
577 - }
595 + // Loop through all retrieved term IDs to filter out empty ones
596 + foreach ( $term_ids as $term_id ) {
597 + // Get the term object for the current term ID
598 + $child_term = get_term( $term_id, $taxonomy );
578 599
579 - if ( empty( $args['orderby'] ) ) {
580 - unset( $args['order'] );
581 - }
600 + // Only include terms that have a non-zero post count
601 + if ( $child_term && $child_term->count > 0 ) {
602 + $non_empty_children[] = $child_term->term_id;
603 + }
604 + }
605 + }
582 606
583 - /**
584 - * Term ID
585 - */
586 - $_term_id = null;
587 - if ( ! empty( $args['term_id'] ) ) {
588 - $_term_id = intval( $args['term_id'] );
589 - unset( $args['term_id'] );
590 - }
607 + // Return the final array of non-empty child term IDs
608 + return $non_empty_children;
609 + }
591 610
592 - // if ( $_term_id == null ) {
593 - // throw new \Exception( __( '$args["term_id"] cannot be null.', 'betterdocs' ) );
594 - // }
611 + /**
612 + * Get all nested child term IDs of a specific parent term in a taxonomy and return as a comma-separated string.
613 + *
614 + * @param string $taxonomy The taxonomy.
615 + * @param int $parent_id The parent term ID.
616 + * @return string The comma-separated term IDs.
617 + */
618 + public function get_child_term_ids_by_parent_id( $taxonomy, $parent_id ) {
619 + $term_ids = $this->get_all_child_term_ids( $taxonomy, $parent_id );
620 + return implode( ',', $term_ids );
621 + }
595 622
596 - /**
597 - * Term Slug for tax_query
598 - */
599 - $_term_slug = '';
600 - if ( ! empty( $args['term_slug'] ) ) {
601 - $_term_slug = trim( $args['term_slug'] );
602 - unset( $args['term_slug'] );
603 - }
623 + public function get_terms_children( $taxonomy, $parent_id ) {
624 + $children = get_term_children( $parent_id, $taxonomy );
625 + $non_empty_children = [];
626 + if ( ! is_wp_error( $children ) && ! empty( $children ) ) {
627 + foreach ( $children as $child_id ) {
628 + $child_term = get_term( $child_id, $taxonomy );
604 629
605 - /**
606 - * @todo old hook
607 - * hook: betterdocs_cat_template_multikb
608 - */
630 + // Only include non-empty terms
631 + if ( $child_term && $child_term->count > 0 ) {
632 + $non_empty_children[] = $child_term;
633 + }
634 + }
635 + }
609 636
610 - $_multiple_kb = apply_filters(
611 - 'betterdocs_enable_multiple_knowledge_base',
612 - isset( $args['multiple_kb'] ) ? (bool) $args['multiple_kb'] : false,
613 - $_origin_args
614 - );
637 + return $non_empty_children;
638 + }
615 639
616 - $_kb_slug = isset( $args['kb_slug'] ) ? trim( $args['kb_slug'] ) : '';
640 + public function get_terms_children_ids( $taxonomy, $parent_id ) {
641 + $children = $this->get_terms_children( $taxonomy, $parent_id );
642 + $term_ids = wp_list_pluck( $children, 'term_id' );
617 643
618 - unset( $args['multiple_kb'] );
619 - unset( $args['kb_slug'] );
644 + return implode( ',', $term_ids );
645 + }
620 646
621 - $tax_query = [
622 - [
623 - 'taxonomy' => 'doc_category',
624 - 'field' => 'slug',
625 - 'terms' => $_term_slug,
626 - 'operator' => 'AND',
627 - 'include_children' => false
628 - ]
629 - ];
647 + public function count_terms_children( $taxonomy, $parent_id ) {
648 + return count( $this->get_terms_children( $taxonomy, $parent_id ) );
649 + }
630 650
631 - if ( isset( $args['tax_query'] ) ) {
632 - $tax_query = $args['tax_query'];
633 - }
651 + public function is_inner_templates() {
652 + if ( is_tax( 'knowledge_base' ) || is_tax( 'doc_category' ) || is_tax( 'doc_tag' ) || is_singular( 'docs' ) ) {
653 + return true;
654 + }
655 + return false;
656 + }
634 657
658 + /**
659 + * Summary of docs_query_args
660 + * @param mixed $args
661 + * @throws \Exception
662 + * @return mixed
663 + */
664 + public function docs_query_args( $args, $filter = [] ) {
665 + $_origin_args = $args;
635 666
667 + $default_args = [
668 + 'post_type' => 'docs'
669 + ];
636 670
637 - $args['tax_query'] = apply_filters(
638 - 'betterdocs_docs_tax_query_args',
639 - $tax_query, $_multiple_kb, $_term_slug, $_kb_slug, $_origin_args, $this->is_inner_templates()
640 - );
641 - /**
642 - * Final parse args
643 - */
644 - $args = wp_parse_args( $args, $default_args );
671 + if ( ! empty( $args['post_type'] ) && trim( $args['post_type'] ) === 'docs_any' ) {
672 + $default_args['post_type'] = 'docs';
673 + $default_args['post_status'] = 'any';
645 674
646 - if ( ! empty( $filter ) ) {
647 - $filter = array_flip( $filter );
648 - $args = array_filter( $args, function ( $item ) use ( $filter ) {
649 - return ! array_key_exists( $item, $filter );
650 - }, ARRAY_FILTER_USE_KEY );
651 - }
675 + unset( $args['post_type'] );
676 + }
652 677
653 - return apply_filters( 'betterdocs_articles_args', $args, $_term_id, $_origin_args );
654 - }
678 + /**
679 + * OrderBy and Order
680 + */
681 + if ( ! isset( $args['orderby'] ) ) {
682 + $_orderby = $this->settings->get( 'alphabetically_order_post' );
683 + $_order = $this->settings->get( 'docs_order', 'ASC' );
684 + } else {
685 + $_orderby = $args['orderby'];
686 + $_order = ! empty( $args['order'] ) ? $args['order'] : 'ASC';
687 + }
655 688
656 - public function faq_terms_query_args( $includes = '', $excludes = '', $args = [] ) {
657 - $_args = [
658 - 'taxonomy' => 'betterdocs_faq_category',
659 - 'meta_key' => 'order',
660 - 'orderby' => 'meta_value_num',
661 - 'order' => 'ASC',
662 - 'include' => $includes,
663 - 'exclude' => $excludes,
664 - 'meta_query' => [
665 - [
666 - 'key' => 'status',
667 - 'value' => 1,
668 - 'compare' => '=='
669 - ]
670 - ]
671 - ];
689 + if ( 'betterdocs_order' != $_orderby ) {
690 + if ( $_orderby === true ) {
691 + $args['orderby'] = 'title';
692 + } else {
693 + $args['orderby'] = $_orderby;
694 + }
672 695
673 - if ( $_args['include'] == 'all' ) {
674 - unset( $_args['include'] );
675 - unset( $_args['exclude'] );
676 - }
696 + $args['order'] = $_order;
697 + } elseif ( 'betterdocs_order' == $_orderby ) {
698 + unset( $args['orderby'] );
699 + }
677 700
678 - if ( empty( $_args['exclude'] ) ) {
679 - unset( $_args['exclude'] );
680 - }
701 + if ( empty( $args['orderby'] ) ) {
702 + unset( $args['order'] );
703 + }
681 704
682 - if ( empty( $_args['include'] ) ) {
683 - unset( $_args['include'] );
684 - }
705 + /**
706 + * Term ID
707 + */
708 + $_term_id = null;
709 + if ( ! empty( $args['term_id'] ) ) {
710 + $_term_id = intval( $args['term_id'] );
711 + unset( $args['term_id'] );
712 + }
685 713
686 - return wp_parse_args( $args, $_args );
687 - }
714 + // if ( $_term_id == null ) {
715 + // throw new \Exception( __( '$args["term_id"] cannot be null.', 'betterdocs' ) );
716 + // }
688 717
689 - public function get_faq_by_term( $term_id ) {
690 - global $wpdb;
718 + /**
719 + * Term Slug for tax_query
720 + */
721 + $_term_slug = '';
722 + if ( ! empty( $args['term_slug'] ) ) {
723 + $_term_slug = trim( $args['term_slug'] );
724 + unset( $args['term_slug'] );
725 + }
691 726
692 - $args = [
693 - 'post_type' => 'betterdocs_faq',
694 - 'post_status' => 'publish',
695 - 'tax_query' => [
696 - [
697 - 'taxonomy' => 'betterdocs_faq_category',
698 - 'field' => 'term_id',
699 - 'terms' => $term_id,
700 - 'operator' => 'AND'
701 - ]
702 - ],
703 - 'posts_per_page' => -1
704 - ];
727 + /**
728 + * @todo old hook
729 + * hook: betterdocs_cat_template_multikb
730 + */
705 731
706 - $args['orderby'] = 'post__in';
707 - $args['post__in'] = $this->get_faq_orders( $term_id );
732 + $_multiple_kb = apply_filters(
733 + 'betterdocs_enable_multiple_knowledge_base',
734 + isset( $args['multiple_kb'] ) ? (bool) $args['multiple_kb'] : false,
735 + $_origin_args
736 + );
708 737
709 - return new WP_Query( $args );
710 - }
738 + $_kb_slug = isset( $args['kb_slug'] ) ? trim( $args['kb_slug'] ) : '';
711 739
712 - public function get_faq_orders( $term_id = null ) {
713 - global $wpdb;
714 - $faq_order = get_term_meta( $term_id, '_betterdocs_faq_order', true );
715 - $faq_order = explode( ',', $faq_order );
740 + unset( $args['multiple_kb'] );
741 + unset( $args['kb_slug'] );
716 742
717 - $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = %d", $term_id );
718 - $query_key = "betterdocs_faq_order_" . md5( $query );
743 + $tax_query = [
744 + [
745 + 'taxonomy' => 'doc_category',
746 + 'field' => 'slug',
747 + 'terms' => $_term_slug,
748 + 'operator' => 'AND',
749 + 'include_children' => false
750 + ]
751 + ];
719 752
720 - if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
721 - return $results;
722 - }
753 + if ( ! isset( $args['orderby'] ) || $args['orderby'] == 'betterdocs_order' ) {
754 + $args['orderby'] = 'post__in';
755 + $args['post__in'] = $this->get_docs_order_by_terms( $_term_id );
756 + }
723 757
724 - if ( ! empty( $faq_order ) ) {
725 - $new_ids = [];
726 - $results = $wpdb->get_results( $query );
758 + if ( isset( $args['tax_query'] ) ) {
759 + $tax_query = $args['tax_query'];
760 + }
727 761
728 - if ( ! is_null( $results ) && ! empty( $results ) && is_array( $results ) ) {
729 - $object_ids = array_filter( $results, function ( $value ) use ( $faq_order ) {
730 - return ! in_array( $value->object_id, $faq_order );
731 - } );
762 + $args['tax_query'] = apply_filters(
763 + 'betterdocs_docs_tax_query_args',
764 + $tax_query,
765 + $_multiple_kb,
766 + $_term_slug,
767 + $_kb_slug,
768 + $_origin_args,
769 + $this->is_inner_templates()
770 + );
771 + /**
772 + * Final parse args
773 + */
774 + $args = wp_parse_args( $args, $default_args );
732 775
733 - if ( ! empty( $object_ids ) ) {
734 - array_walk( $object_ids, function ( $value ) use ( &$new_ids ) {
735 - $new_ids[] = $value->object_id;
736 - } );
737 - }
738 - }
776 + if ( ! empty( $filter ) ) {
777 + $filter = array_flip( $filter );
778 + $args = array_filter(
779 + $args,
780 + function ( $item ) use ( $filter ) {
781 + return ! array_key_exists( $item, $filter );
782 + },
783 + ARRAY_FILTER_USE_KEY
784 + );
785 + }
739 786
740 - $faq_order = array_merge( $new_ids, $faq_order );
741 - }
787 + return apply_filters( 'betterdocs_articles_args', $args, $_term_id, $_origin_args );
788 + }
742 789
743 - $this->database->set_cache( $query_key, $faq_order, 1 );
790 + public function faq_terms_query_args( $includes = '', $excludes = '', $args = [] ) {
791 + $_args = [
792 + 'taxonomy' => 'betterdocs_faq_category',
793 + 'meta_key' => 'order',
794 + 'orderby' => 'meta_value_num',
795 + 'order' => 'ASC',
796 + 'include' => $includes,
797 + 'exclude' => $excludes,
798 + 'meta_query' => [
799 + [
800 + 'key' => 'status',
801 + 'value' => 1,
802 + 'compare' => '=='
803 + ]
804 + ]
805 + ];
744 806
745 - return $faq_order;
746 - }
807 + if ( $_args['include'] == 'all' ) {
808 + unset( $_args['include'] );
809 + unset( $_args['exclude'] );
810 + }
747 811
748 - public function get_faq_terms( $terms = [] ) {
749 - $_terms = get_terms( [
750 - 'taxonomy' => 'betterdocs_faq_category',
751 - 'hide_empty' => true,
752 - 'orderby' => 'name',
753 - 'order' => 'ASC',
754 - 'meta_query' => [
755 - [
756 - 'key' => 'status',
757 - 'value' => 1,
758 - 'compare' => '=='
759 - ]
760 - ]
761 - ] );
812 + if ( empty( $_args['exclude'] ) ) {
813 + unset( $_args['exclude'] );
814 + }
762 815
763 - if ( ! is_wp_error( $_terms ) ) {
764 - foreach ( $_terms as $term ) {
765 - $terms[$term->term_id] = $term->name;
766 - }
767 - }
816 + if ( empty( $_args['include'] ) ) {
817 + unset( $_args['include'] );
818 + }
768 819
769 - return $terms;
770 - }
820 + return wp_parse_args( $args, $_args );
821 + }
771 822
772 - public function get_docs_count( $term, $nested_subcategory = false, $args = [] ) {
773 - $counts = isset( $term->count ) ? $term->count : 0;
823 + public function get_faq_by_term( $term_id ) {
824 + global $wpdb;
774 825
775 - if ( $nested_subcategory == false ) {
776 - return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
777 - }
826 + $args = [
827 + 'post_type' => 'betterdocs_faq',
828 + 'post_status' => 'publish',
829 + 'tax_query' => [
830 + [
831 + 'taxonomy' => 'betterdocs_faq_category',
832 + 'field' => 'term_id',
833 + 'terms' => $term_id,
834 + 'operator' => 'AND'
835 + ]
836 + ],
837 + 'posts_per_page' => -1
838 + ];
778 839
779 - $_child_terms_docs_ids = $this->get_doc_ids_by_term( $term, null, $nested_subcategory );
780 - if ( is_array( $_child_terms_docs_ids ) ) {
781 - $counts = count( $_child_terms_docs_ids );
782 - }
840 + $args['orderby'] = 'post__in';
841 + $args['post__in'] = $this->get_faq_orders( $term_id );
783 842
784 - return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
785 - }
843 + return new WP_Query( $args );
844 + }
786 845
787 - public function get_doc_ids_by_term( $term, $optional = null, $nested_subcategory = false ) {
788 - $args = [ 'include' => $term->term_id ];
789 - if( $nested_subcategory ) {
846 + public function get_faq_orders( $term_id = null ) {
847 + global $wpdb;
848 + $faq_order = get_term_meta( $term_id, '_betterdocs_faq_order', true );
849 + $faq_order = explode( ',', $faq_order );
850 +
851 + $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = %d", $term_id );
852 + $query_key = 'betterdocs_faq_order_' . md5( $query );
853 +
854 + if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
855 + return $results;
856 + }
857 +
858 + if ( ! empty( $faq_order ) ) {
859 + $new_ids = [];
860 + $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
861 +
862 + if ( ! is_null( $results ) && ! empty( $results ) && is_array( $results ) ) {
863 + $object_ids = array_filter(
864 + $results,
865 + function ( $value ) use ( $faq_order ) {
866 + return ! in_array( $value->object_id, $faq_order );
867 + }
868 + );
869 +
870 + if ( ! empty( $object_ids ) ) {
871 + array_walk(
872 + $object_ids,
873 + function ( $value ) use ( &$new_ids ) {
874 + $new_ids[] = $value->object_id;
875 + }
876 + );
877 + }
878 + }
879 +
880 + $faq_order = array_merge( $new_ids, $faq_order );
881 + }
882 +
883 + $this->database->set_cache( $query_key, $faq_order, 1 );
884 +
885 + return $faq_order;
886 + }
887 +
888 + public function get_faq_terms( $terms = [] ) {
889 + $_terms = get_terms(
890 + [
891 + 'taxonomy' => 'betterdocs_faq_category',
892 + 'hide_empty' => true,
893 + 'orderby' => 'name',
894 + 'order' => 'ASC',
895 + 'meta_query' => [
896 + [
897 + 'key' => 'status',
898 + 'value' => 1,
899 + 'compare' => '=='
900 + ]
901 + ]
902 + ]
903 + );
904 +
905 + if ( ! is_wp_error( $_terms ) ) {
906 + foreach ( $_terms as $term ) {
907 + $terms[ $term->term_id ] = $term->name;
908 + }
909 + }
910 +
911 + return $terms;
912 + }
913 +
914 + public function get_doc_terms( $terms = [] ) {
915 + $_terms = get_terms(
916 + [
917 + 'taxonomy' => 'doc_category',
918 + 'hide_empty' => true,
919 + 'orderby' => 'name',
920 + 'order' => 'ASC',
921 + ]
922 + );
923 +
924 + if ( ! is_wp_error( $_terms ) ) {
925 + foreach ( $_terms as $term ) {
926 + $terms[ $term->term_id ] = $term->name;
927 + }
928 + }
929 +
930 + return $terms;
931 + }
932 +
933 +
934 + public function get_docs_count( $term, $nested_subcategory = false, $args = [] ) {
935 + $counts = isset( $term->count ) ? $term->count : 0;
936 +
937 + if ( $nested_subcategory == false ) {
938 + return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
939 + }
940 +
941 + $_child_terms_docs_ids = $this->get_doc_ids_by_term( $term, null, $nested_subcategory );
942 + if ( is_array( $_child_terms_docs_ids ) ) {
943 + $counts = count( $_child_terms_docs_ids );
944 + }
945 +
946 + return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
947 + }
948 +
949 + public function get_doc_ids_by_term( $term, $optional = null, $nested_subcategory = false ) {
950 + $args = ['include' => $term->term_id];
951 + if ( $nested_subcategory ) {
790 952 $args['child_of'] = $term->term_id;
791 953 unset( $args['include'] );
792 954 }
793 955 $_child_terms = get_terms( $term->taxonomy, $args );
@@ -814,6 +976,239 @@
814 976
815 977 $_status = get_post_status( $doc_id );
816 978 return $_status == 'private' || is_post_publicly_viewable( $doc_id );
817 979 } );
818 - }
980 + }
981 +
982 + /**
983 + * Get the common query arguments for WP_Query.
984 + *
985 + * @param string $terms The taxonomy.
986 + * @param string $term_slug The taxonomy term slug.
987 + * @param array $additional_args Additional arguments to merge with the common arguments.
988 + * @return array The query arguments.
989 + */
990 + private function tax_query_args( $terms, $term_slug, $additional_args = array() ) {
991 + $common_args = array(
992 + 'post_type' => 'docs',
993 + 'post_status' => 'publish',
994 + 'tax_query' => array(
995 + array(
996 + 'taxonomy' => $terms,
997 + 'field' => 'slug',
998 + 'terms' => $term_slug,
999 + ),
1000 + ),
1001 + );
1002 + return array_merge( $common_args, $additional_args );
1003 + }
1004 +
1005 + /**
1006 + * Get the latest updated date for a specific taxonomy term.
1007 + *
1008 + * @param string $terms The taxonomy.
1009 + * @param string $term_slug The taxonomy term slug.
1010 + * @return string|null The latest modified date or null if no posts found.
1011 + */
1012 + public function latest_updated_date( $terms, $term_slug ) {
1013 + $args = $this->tax_query_args(
1014 + $terms,
1015 + $term_slug,
1016 + array(
1017 + 'posts_per_page' => 1,
1018 + 'orderby' => 'modified',
1019 + 'order' => 'DESC',
1020 + )
1021 + );
1022 +
1023 + $query = new WP_Query( $args );
1024 +
1025 + if ( $query->have_posts() ) {
1026 + while ( $query->have_posts() ) {
1027 + $query->the_post();
1028 + $latest_post = get_the_modified_date();
1029 + wp_reset_postdata();
1030 + return $latest_post;
1031 + }
1032 + } else {
1033 + return null;
1034 + }
1035 + }
1036 +
1037 + /**
1038 + * Check if there are any new posts within the last 7 days for a specific taxonomy term.
1039 + *
1040 + * @param string $terms The taxonomy.
1041 + * @param string $term_slug The taxonomy term slug.
1042 + * @return bool True if there are new posts, false otherwise.
1043 + */
1044 + public function check_new_posts( $terms, $term_slug ) {
1045 + $date_7_days_ago = date( 'Y-m-d H:i:s', strtotime( '-7 days' ) );
1046 +
1047 + $args = $this->tax_query_args(
1048 + $terms,
1049 + $term_slug,
1050 + array(
1051 + 'posts_per_page' => 1,
1052 + 'orderby' => 'modified',
1053 + 'order' => 'DESC',
1054 + 'date_query' => array(
1055 + array(
1056 + 'after' => $date_7_days_ago,
1057 + 'inclusive' => true,
1058 + ),
1059 + ),
1060 + )
1061 + );
1062 +
1063 + $query = new WP_Query( $args );
1064 +
1065 + $has_new_posts = $query->have_posts();
1066 +
1067 + wp_reset_postdata();
1068 +
1069 + return $has_new_posts;
1070 + }
1071 +
1072 + public function insert_search_keyword( $search_input, $input_not_found ) {
1073 + if ( empty( $search_input ) ) {
1074 + return false;
1075 + }
1076 +
1077 + global $wpdb;
1078 + $search = $wpdb->get_results(
1079 + $wpdb->prepare(
1080 + "SELECT *
1081 + FROM {$wpdb->prefix}betterdocs_search_keyword
1082 + WHERE keyword = %s",
1083 + $search_input
1084 + )
1085 + );
1086 +
1087 + if ( ! empty( $search ) ) {
1088 + $search_log = $wpdb->get_results(
1089 + $wpdb->prepare(
1090 + "SELECT *
1091 + FROM {$wpdb->prefix}betterdocs_search_log
1092 + WHERE created_at = %s AND keyword_id = %d",
1093 + date( 'Y-m-d' ),
1094 + $search[0]->id
1095 + )
1096 + );
1097 +
1098 + if ( ! empty( $search_log ) ) {
1099 + if ( ! empty( $input_not_found ) ) {
1100 + $tbl_field = 'not_found_count';
1101 + $count = $search_log[0]->not_found_count + 1;
1102 + } else {
1103 + $tbl_field = 'count';
1104 + $count = $search_log[0]->count + 1;
1105 + }
1106 + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1107 + $insert = $wpdb->query(
1108 + $wpdb->prepare(
1109 + "UPDATE {$wpdb->prefix}betterdocs_search_log
1110 + SET " . $tbl_field . ' = ' . $count . '
1111 + WHERE created_at = %s AND keyword_id = %d',
1112 + $search_log[0]->created_at,
1113 + $search_log[0]->keyword_id
1114 + )
1115 + );
1116 + } else {
1117 + if ( ! empty( $input_not_found ) ) {
1118 + $count = 0;
1119 + $not_found_count = 1;
1120 + } else {
1121 + $count = 1;
1122 + $not_found_count = 0;
1123 + }
1124 + $insert = $wpdb->query(
1125 + $wpdb->prepare(
1126 + "INSERT INTO {$wpdb->prefix}betterdocs_search_log
1127 + ( keyword_id, count, not_found_count, created_at )
1128 + VALUES ( %d, %d, %d, %s )",
1129 + [
1130 + $search[0]->id,
1131 + $count,
1132 + $not_found_count,
1133 + date( 'Y-m-d' )
1134 + ]
1135 + )
1136 + );
1137 + }
1138 + } else {
1139 + $insert = $wpdb->query(
1140 + $wpdb->prepare(
1141 + "INSERT INTO {$wpdb->prefix}betterdocs_search_keyword
1142 + ( keyword )
1143 + VALUES ( %s )",
1144 + [
1145 + $search_input
1146 + ]
1147 + )
1148 + );
1149 +
1150 + if ( $insert ) {
1151 + if ( ! empty( $input_not_found ) ) {
1152 + $count = 0;
1153 + $not_found_count = 1;
1154 + } else {
1155 + $count = 1;
1156 + $not_found_count = 0;
1157 + }
1158 + $insert = $wpdb->query(
1159 + $wpdb->prepare(
1160 + "INSERT INTO {$wpdb->prefix}betterdocs_search_log
1161 + ( keyword_id, count, not_found_count, created_at )
1162 + VALUES ( %d, %d, %d, %s )",
1163 + [
1164 + $wpdb->insert_id,
1165 + $count,
1166 + $not_found_count,
1167 + date( 'Y-m-d' )
1168 + ]
1169 + )
1170 + );
1171 + }
1172 + }
1173 + return $insert;
1174 + }
1175 +
1176 + /**
1177 + * Counts the number of 'doc_category' terms assigned to a specific 'knowledge_base' by slug.
1178 + *
1179 + * This method retrieves all terms in the 'doc_category' taxonomy and checks the term meta
1180 + * 'doc_category_knowledge_base' to determine how many 'doc_category' terms are associated
1181 + * with a given 'knowledge_base' slug.
1182 + *
1183 + * @param string $knowledge_base_slug The slug of the 'knowledge_base' to count the assignments for.
1184 + *
1185 + * @return int The count of 'doc_category' terms assigned to the specified 'knowledge_base'.
1186 + */
1187 + public function count_doc_categories_for_knowledge_base( $knowledge_base_slug ) {
1188 + $count = 0;
1189 +
1190 + $doc_categories = get_terms(
1191 + array(
1192 + 'taxonomy' => 'doc_category',
1193 + 'hide_empty' => true,
1194 + )
1195 + );
1196 +
1197 + if ( ! empty( $doc_categories ) && ! is_wp_error( $doc_categories ) ) {
1198 + foreach ( $doc_categories as $term ) {
1199 + $meta_value = get_term_meta( $term->term_id, 'doc_category_knowledge_base', true );
1200 +
1201 + if ( $meta_value ) {
1202 + $knowledge_bases = maybe_unserialize( $meta_value );
1203 +
1204 + // Check if the specific knowledge base slug is present in the meta value array
1205 + if ( is_array( $knowledge_bases ) && in_array( $knowledge_base_slug, $knowledge_bases ) ) {
1206 + ++$count;
1207 + }
1208 + }
1209 + }
1210 + }
1211 +
1212 + return $count;
1213 + }
819 1214 }