PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.5.4
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.5.4
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 / Admin / WPExporter.php

WPExporter.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.5.4, at includes/Admin/WPExporter.php

397 lines 12.7 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\Admin;
4
5 use WPDeveloper\BetterDocs\Admin\ExportDefaults;
6 use WP_Term;
7 use wpdb;
8
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly.
12 }
13
14 #[\AllowDynamicProperties]
15 class WPExporter {
16 /**
17 * @var array
18 */
19 private $args;
20
21 /**
22 * @var wpdb
23 */
24 private $wpdb;
25
26 public function __construct( array $args = [] ) {
27 global $wpdb;
28
29 $this->args = wp_parse_args($args, ExportDefaults::get_default_args());
30
31 $this->wpdb = $wpdb;
32 }
33
34 public function exporter_ids(): array {
35 // Build the base query
36 $query_parts = $this->build_base_query();
37 $where = $query_parts['where'];
38 $join = $query_parts['join'];
39
40 // Handle post status
41 $where .= $this->build_status_condition();
42
43 // Handle specific posts selection
44 if ( ! empty( $this->args['post__in'] ) ) {
45 $where .= $this->build_post_in_condition();
46 }
47
48 // Handle category terms
49 if ( isset( $this->args['category_terms'] ) ) {
50 $category_query = $this->build_category_query();
51 $join .= $category_query['join'];
52 $where .= $category_query['where'];
53 }
54
55 // Handle knowledge base terms
56 if ( isset( $this->args['kb_terms'] ) ) {
57 $kb_query = $this->build_kb_query();
58 $join .= $kb_query['join'];
59 $where .= $kb_query['where'];
60 }
61
62 // Handle additional filters (author, dates, meta)
63 $where .= $this->build_additional_filters();
64
65 // Get the main doc post IDs
66 $post_ids = $this->wpdb->get_col( "SELECT DISTINCT {$this->wpdb->posts}.ID FROM {$this->wpdb->posts} $join WHERE $where" );
67
68 // Handle FAQ posts separately
69 $faq_post_ids = [];
70 if ( ! empty( $this->args['include_faq'] ) ) {
71 $faq_post_ids = $this->get_faq_posts();
72 }
73
74 // Combine post IDs
75 $all_post_ids = array_merge( $post_ids, $faq_post_ids );
76
77 $all_post_ids = WPMLSupport::expand_with_translations( $all_post_ids );
78
79 // Handle featured images
80 $thumbnail_ids = $this->get_thumbnail_ids( $all_post_ids );
81
82 // Generate final post IDs array
83 return array_unique(array_merge($all_post_ids, $thumbnail_ids));
84
85 }
86
87 // private function handle_glossaries_export(): array {
88 public function get_glossary_term_ids(): array {
89 if ( isset( $this->args['glossary_terms'] ) && ( count( $this->args['glossary_terms'] ) > 0 ) ) {
90 $glossary_term_ids = [];
91 foreach( $this->args['glossary_terms'] as $glossary_slug ) {
92 $term_object = get_term_by('slug', $glossary_slug , 'glossaries');
93 if( isset( $term_object->term_id ) && ! empty( $term_object->term_id ) ){
94 array_push($glossary_term_ids, $term_object->term_id);
95 }
96 }
97 } else {
98 $glossary_term_ids = $this->wpdb->get_col( "SELECT term_id from {$this->wpdb->term_taxonomy} where taxonomy='{$this->args['content']}';" );
99 }
100
101 return $glossary_term_ids;
102 }
103
104 public function build_base_query(): array {
105 $where = $this->wpdb->prepare("{$this->wpdb->posts}.post_type = %s", 'docs');
106 return [
107 'where' => $where,
108 'join' => ''
109 ];
110 }
111
112 public function build_status_condition(): string {
113 if ($this->args['status']) {
114 return $this->wpdb->prepare(" AND {$this->wpdb->posts}.post_status = %s", $this->args['status']);
115 }
116 return " AND {$this->wpdb->posts}.post_status != 'auto-draft'";
117 }
118
119 public function build_post_in_condition(): string {
120 $ids = $this->args['post__in'];
121 $ids_placeholder = implode(', ', array_fill(0, count($ids), '%d'));
122 return $this->wpdb->prepare(" AND {$this->wpdb->posts}.ID IN ($ids_placeholder)", $ids);
123 }
124
125 public function build_category_query(): array {
126 $join = "INNER JOIN {$this->wpdb->term_relationships} tr ON ({$this->wpdb->posts}.ID = tr.object_id)
127 INNER JOIN {$this->wpdb->term_taxonomy} tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)";
128
129 $where = " AND tt.taxonomy = 'doc_category'";
130
131 if ( ! empty( $this->args['category_terms'] ) ) {
132 $term_ids = [];
133 foreach ( $this->args['category_terms'] as $term_id ) {
134 $term = term_exists( $term_id, 'doc_category' );
135 if ( $term ) {
136 $term_ids[] = $term['term_taxonomy_id'];
137 }
138 }
139
140 if ( ! empty( $term_ids ) ) {
141 $term_placeholder = implode( ', ', array_fill( 0, count( $term_ids ), '%d' ) );
142 $where .= $this->wpdb->prepare( " AND tt.term_taxonomy_id IN ($term_placeholder)", $term_ids );
143 }
144 }
145
146 return [
147 'join' => $join,
148 'where' => $where
149 ];
150 }
151
152 public function build_kb_query(): array {
153 $join = "INNER JOIN {$this->wpdb->term_relationships} tr ON ({$this->wpdb->posts}.ID = tr.object_id)
154 INNER JOIN {$this->wpdb->term_taxonomy} tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)";
155
156 $where = " AND tt.taxonomy = 'knowledge_base'";
157
158 if ( ! empty( $this->args['kb_terms'] ) ) {
159 $term_ids = [];
160 foreach ( $this->args['kb_terms'] as $term_id ) {
161 $term = term_exists( $term_id, 'knowledge_base' );
162 if ( $term ) {
163 $term_ids[] = $term['term_taxonomy_id'];
164 }
165 }
166
167 if ( ! empty( $term_ids ) ) {
168 $term_placeholder = implode( ', ', array_fill( 0, count( $term_ids ), '%d' ) );
169 $where .= $this->wpdb->prepare( " AND tt.term_taxonomy_id IN ($term_placeholder)", $term_ids );
170 }
171 }
172
173 return [
174 'join' => $join,
175 'where' => $where
176 ];
177 }
178
179 public function build_additional_filters(): string {
180 $where = '';
181
182 if ( $this->args['author'] ) {
183 $where .= $this->wpdb->prepare( " AND {$this->wpdb->posts}.post_author = %d", $this->args['author'] );
184 }
185
186 if ( $this->args['start_date'] ) {
187 $where .= $this->wpdb->prepare(
188 " AND {$this->wpdb->posts}.post_date >= %s",
189 gmdate( 'Y-m-d', strtotime( $this->args['start_date'] ) )
190 );
191 }
192
193 if ( $this->args['end_date'] ) {
194 $where .= $this->wpdb->prepare(
195 " AND {$this->wpdb->posts}.post_date < %s",
196 gmdate( 'Y-m-d', strtotime( '+1 month', strtotime( $this->args['end_date'] ) ) )
197 );
198 }
199
200 return $where;
201 }
202
203 public function get_faq_posts(): array {
204 return get_posts([
205 'numberposts' => -1,
206 'post_type' => 'betterdocs_faq',
207 'fields' => 'ids',
208 'post_status' => 'publish',
209 'suppress_filters' => true,
210 ]);
211 }
212
213 public function get_thumbnail_ids(array $post_ids): array {
214 $thumbnail_ids = [];
215
216 if ( ! empty( $this->args['include_post_featured_image_as_attachment'] ) ) {
217 foreach ( $post_ids as $post_id ) {
218 $thumbnail_id = get_post_meta( $post_id, '_thumbnail_id', true );
219 if ( $thumbnail_id && ! in_array( $thumbnail_id, $post_ids, true ) ) {
220 $thumbnail_ids[] = $thumbnail_id;
221 }
222 }
223 }
224
225 return $thumbnail_ids;
226 }
227
228 /**
229 * Return tabulation characters, by `$columns`.
230 *
231 * @param int $columns
232 *
233 * @return string
234 */
235 public function indent( int $columns = 1 ): string {
236
237 $output = str_repeat( "\t", $columns );
238
239 return (string) $output;
240 }
241
242 /**
243 * Retrieve terms associated with the specified object IDs and sort them based on term meta.
244 *
245 * @param array $post_ids An array of object IDs.
246 * @return array An array of WP_Term objects sorted based on term meta.
247 */
248 public function get_terms(array $post_ids = []) {
249 // Get the object taxonomies
250 $taxonomies = get_object_taxonomies($this->args['content']);
251
252 $terms = [];
253
254 // Handle glossaries
255 if ($this->args['content'] == 'glossaries') {
256 $terms = get_terms([
257 'taxonomy' => 'glossaries',
258 'include' => $post_ids,
259 'hide_empty' => false,
260 ]);
261
262 if (is_wp_error($terms)) {
263 return [];
264 }
265
266 // Include parent terms for hierarchical glossaries
267 $parent_terms = [];
268 foreach ($terms as $term) {
269 if (!empty($term->parent)) {
270 $parent = get_term($term->parent, 'glossaries');
271 if (!is_wp_error($parent)) {
272 $parent_terms[] = $parent;
273 }
274 }
275 }
276
277 if (!empty($parent_terms)) {
278 $terms = array_merge($terms, $parent_terms);
279 $terms = array_unique($terms, SORT_REGULAR);
280 }
281 } elseif ($this->args['content'] == 'docs') { // Handle docs
282 if (isset($this->args['selected_docs']) && !empty($this->args['selected_docs'])) {
283 if ($this->args['selected_docs'][0] == 'all') {
284 // Fetch all terms
285 $terms = get_terms([
286 'taxonomy' => $taxonomies,
287 'hide_empty' => false,
288 ]);
289 } else {
290 // Fetch terms for specific post IDs
291 $terms = wp_get_object_terms($this->args['selected_docs'], $taxonomies);
292 }
293 }
294 } elseif (in_array($this->args['content'], ['knowledge_base', 'doc_category'])) {
295 // Handle both knowledge_base and doc_category cases
296 $terms_key = $this->args['content'] == 'doc_category' ? 'category_terms' : 'kb_terms';
297
298 if (isset($this->args[$terms_key]) && !empty($this->args[$terms_key])) {
299 $terms_arg = [
300 'taxonomy' => $taxonomies,
301 'hide_empty' => false,
302 ];
303
304 if ($this->args[$terms_key][0] !== 'all') {
305 $terms_arg['slug'] = $this->args[$terms_key];
306 }
307
308 $terms = get_terms($terms_arg);
309 }
310 }
311
312 // Include both parent and child terms for hierarchical structures
313 if (!empty($terms) && is_array($terms)) {
314 $additional_terms = [];
315
316 foreach ($terms as $term) {
317 // Get parent terms
318 if (!empty($term->parent)) {
319 $ancestors = get_ancestors($term->term_id, $term->taxonomy, 'taxonomy');
320 foreach ($ancestors as $ancestor_id) {
321 $parent = get_term($ancestor_id, $term->taxonomy);
322 if (!is_wp_error($parent)) {
323 $additional_terms[] = $parent;
324 }
325 }
326 }
327
328 // Get child terms
329 $children = get_term_children($term->term_id, $term->taxonomy);
330 if (!is_wp_error($children)) {
331 foreach ($children as $child_id) {
332 $child = get_term($child_id, $term->taxonomy);
333 if (!is_wp_error($child)) {
334 $additional_terms[] = $child;
335 }
336 }
337 }
338 }
339
340 if (!empty($additional_terms)) {
341 $terms = array_merge($terms, $additional_terms);
342 $terms = array_unique($terms, SORT_REGULAR);
343 }
344 }
345
346 // Sort terms if not empty
347 if (!empty($terms) && is_array($terms)) {
348 usort($terms, array($this, 'compare_terms_by_meta'));
349 }
350
351 return $terms;
352 }
353
354 /**
355 * Compare terms based on their associated term meta values.
356 *
357 * @param WP_Term $a The first term object.
358 * @param WP_Term $b The second term object.
359 * @return int Returns a negative value if $a is less than $b,
360 * a positive value if $a is greater than $b, or 0 if they are equal.
361 * Additionally, prioritize sorting terms by taxonomy order,
362 * with 'doc_category' terms appearing before other taxonomy terms.
363 */
364 public function compare_terms_by_meta( $a, $b ) {
365 // Define the order of taxonomies
366 $taxonomy_order = array(
367 'doc_category' => 0,
368 'knowledge_base' => 1,
369 'doc_tag' => 2,
370 );
371
372 // Get the taxonomy order for terms $a and $b
373 $order_a = isset( $taxonomy_order[ $a->taxonomy ] ) ? $taxonomy_order[ $a->taxonomy ] : PHP_INT_MAX;
374 $order_b = isset( $taxonomy_order[ $b->taxonomy ] ) ? $taxonomy_order[ $b->taxonomy ] : PHP_INT_MAX;
375
376 // If the taxonomies have different order, sort by order
377 if ( $order_a !== $order_b ) {
378 return $order_a - $order_b;
379 }
380
381 // If the taxonomies have the same order, sort by meta value
382 $taxonomy_order_meta = array(
383 'doc_category' => 'doc_category_order',
384 'knowledge_base' => 'kb_order'
385 );
386
387 if ( isset( $taxonomy_order_meta[ $a->taxonomy ] ) && isset( $taxonomy_order_meta[ $b->taxonomy ] ) ) {
388 $meta_a = intval( get_term_meta( $a->term_id, $taxonomy_order_meta[ $a->taxonomy ], true ) );
389 $meta_b = intval( get_term_meta( $b->term_id, $taxonomy_order_meta[ $b->taxonomy ], true ) );
390
391 return $meta_a - $meta_b;
392 }
393
394 return 0; // Default to no sorting if meta keys are not defined
395 }
396 }
397