PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.7.3
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.7.3
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 / CSVExporter.php

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

487 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WPDeveloper\BetterDocs\Admin;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit; // Exit if accessed directly.
6 }
7
8 #[\AllowDynamicProperties]
9 class CSVExporter {
10 private static $default_args = [
11 'content' => 'docs',
12 'author' => false,
13 'category' => false,
14 'start_date' => false,
15 'end_date' => false,
16 'status' => false,
17 'offset' => 0,
18 'limit' => -1,
19 'meta_query' => [], // If specified `meta_key` then will include all post(s) that have this meta_key.
20 'query_args' => []
21 ];
22
23 /**
24 * @var array
25 */
26 private $args;
27
28 /**
29 * @var wpdb
30 */
31 private $wpdb;
32
33 public function __construct( array $args = [] ) {
34 global $wpdb;
35
36 $this->args = wp_parse_args( $args, self::$default_args );
37
38 $this->wpdb = $wpdb;
39 }
40
41 public function combine_csv_data($csv_data_array) {
42 // Combine headers
43 $headers_combined = $csv_data_array[0][0];
44
45 foreach ($csv_data_array as $csv_data) {
46 $headers_combined = array_merge($headers_combined, array_slice($csv_data[0], 1));
47 }
48
49 $csv_data_combined = [$headers_combined];
50
51 // Combine data
52 for ($i = 1; $i < count($csv_data_array[0]); $i++) {
53 $combined_row = [];
54 foreach ($csv_data_array as $csv_data) {
55 $combined_row = array_merge($combined_row, array_fill(0, count($csv_data_combined[0]) - count($combined_row)), [$csv_data[$i][0]], array_slice($csv_data[$i], 1));
56 }
57 $csv_data_combined[] = $combined_row;
58 }
59
60 return $csv_data_combined;
61 }
62
63
64 public function run(): array {
65 $post_args = [
66 'post_type' => 'docs',
67 'posts_per_page' => -1
68 ];
69
70 if( $this->args['include_faq'] ) { //include betterdocs_faq when 'all' are selected from 'docs' or 'doc categories'
71 $post_args['post_type'] = ['docs', 'betterdocs_faq'];
72 }
73
74 if ( isset($this->args['post__in']) ) {
75 if( $this->args['include_faq'] ){ //include betterdocs_faq 'ids' when specific id's are selected from 'docs'
76 $faq_posts_ids = get_posts([
77 'numberposts' => -1,
78 'post_type' => 'betterdocs_faq',
79 'fields' => 'ids'
80 ]);
81 $this->args['post__in'] = array_merge( isset( $this->args['post__in'] ) ? $this->args['post__in'] : [] , $faq_posts_ids );
82 }
83
84 $post_args['post__in'] = $this->args['post__in'];
85 }
86
87 if ( isset($this->args['category_terms']) ) {
88 $post_args['tax_query'] = [
89 [
90 'taxonomy' => 'doc_category',
91 'field' => 'slug',
92 'terms' => $this->args['category_terms'],
93 ],
94 ];
95
96 if( $this->args['include_faq'] ) { //include betterdocs_faq when specific 'doc categories' are selected
97 $post_args['tax_query']['relation'] = 'OR';
98 $faq_category_slugs = get_terms([
99 'taxonomy' => 'betterdocs_faq_category',
100 'fields' => 'slugs',
101 ]);
102 $post_args['tax_query'][] = [
103 'taxonomy' => 'betterdocs_faq_category',
104 'field' => 'slug',
105 'terms' => $faq_category_slugs,
106 'operator' => 'IN',
107 ];
108 }
109 }
110
111 if ( isset($this->args['kb_terms']) ) {
112 $post_args['tax_query'] = [
113 [
114 'taxonomy' => 'knowledge_base',
115 'field' => 'slug',
116 'terms' => $this->args['kb_terms'],
117 ],
118 ];
119 }
120
121 if( $this->args['content'] == 'glossaries') { //for glossaries terms
122 $posts = get_terms( [
123 'taxonomy' => 'glossaries',
124 'hide_empty' => false,
125 ] );
126 } else {
127 $posts = get_posts($post_args);
128 }
129
130 $post_ids = array_map(function ($post) {
131 if( $this->args['content'] == 'glossaries' ) { //for glossaries terms
132 return $post->term_id;
133 }
134 return $post->ID;
135 }, $posts);
136
137 $csv_data_terms = $this->get_terms_csv_data($post_ids);
138
139 $csv_data_author = $this->authors_list($post_ids);
140
141 $csv_data_posts = $this->get_posts_csv_data($posts);
142
143
144 // Combine headers
145 $headers_combined = array_merge($csv_data_posts[0], array_slice($csv_data_author[0], 1), array_slice($csv_data_terms[0], 1));
146
147 // Initialize the combined array with headers
148 $csv_data_combined = [$headers_combined];
149
150 // Combine term data with existing combined array
151 for ($i = 1; $i < count($csv_data_posts); $i++) {
152 $combined_row = array_merge($csv_data_posts[$i], array_fill(0, count($headers_combined) - count($csv_data_posts[$i]), ''));
153 $csv_data_combined[] = $combined_row;
154 }
155
156 // Calculate the starting index for author data
157 $author_start_index = count($csv_data_posts[0]);
158
159 // Combine author data with existing combined array
160 for ($i = 1; $i < count($csv_data_author); $i++) {
161 $combined_row = array_merge([$csv_data_author[$i][0]], array_fill(1, $author_start_index - 1, ''), array_slice($csv_data_author[$i], 1));
162 $csv_data_combined[] = $combined_row;
163 }
164
165 // Calculate the starting index for post data
166 $terms_start_index = $author_start_index + count($csv_data_author[0]) - 1;
167
168 // Combine post data with existing combined array
169 for ($i = 1; $i < count($csv_data_terms); $i++) {
170 $combined_row = array_merge([$csv_data_terms[$i][0]], array_fill(1, $terms_start_index - 1, ''), array_slice($csv_data_terms[$i], 1));
171 $csv_data_combined[] = $combined_row;
172 }
173
174 $filename = 'betterdocs.' . date('Y-m-d') . '.csv';
175 $csv_content = $this->generate_csv($csv_data_combined);
176
177 return [
178 'success' => true,
179 'data' => [
180 'filename' => $filename,
181 'filetype' => 'text/csv',
182 'download' => $csv_content,
183 ],
184 ];
185 }
186
187 /**
188 * Retrieve terms associated with the specified object IDs and sort them based on term meta.
189 *
190 * @param array $post_ids An array of object IDs.
191 * @return array An array of WP_Term objects sorted based on term meta.
192 */
193 private function get_terms( array $post_ids, $include_faq = false ) {
194 $post_types = [
195 'docs'
196 ];
197
198 if( $include_faq ) {
199 array_push($post_types, 'betterdocs_faq');
200 }
201
202 // Get the object taxonomies
203 $taxonomies = get_object_taxonomies( $post_types );
204
205 // Get the object terms with parent terms coming before their child terms
206 $terms = wp_get_object_terms( $post_ids, $taxonomies);
207
208 usort( $terms, array( $this, 'compare_terms_by_meta' ) );
209
210 return $terms;
211 }
212
213 /**
214 * Compare terms based on their associated term meta values.
215 *
216 * @param WP_Term $a The first term object.
217 * @param WP_Term $b The second term object.
218 * @return int Returns a negative value if $a is less than $b,
219 * a positive value if $a is greater than $b, or 0 if they are equal.
220 * Additionally, prioritize sorting terms by taxonomy order,
221 * with 'doc_category' terms appearing before other taxonomy terms.
222 */
223 public function compare_terms_by_meta($a, $b) {
224 // Define the order of taxonomies
225 $taxonomy_order = array(
226 'doc_category' => 0,
227 'knowledge_base' => 1,
228 'doc_tag' => 2,
229 );
230
231 // Get the taxonomy order for terms $a and $b
232 $order_a = isset($taxonomy_order[$a->taxonomy]) ? $taxonomy_order[$a->taxonomy] : PHP_INT_MAX;
233 $order_b = isset($taxonomy_order[$b->taxonomy]) ? $taxonomy_order[$b->taxonomy] : PHP_INT_MAX;
234
235 // If the taxonomies have different order, sort by order
236 if ($order_a !== $order_b) {
237 return $order_a - $order_b;
238 }
239
240 // If the taxonomies have the same order, sort by meta value
241 $taxonomy_order_meta = array(
242 'doc_category' => 'doc_category_order',
243 'knowledge_base' => 'kb_order'
244 );
245
246 if (isset($taxonomy_order_meta[$a->taxonomy]) && isset($taxonomy_order_meta[$b->taxonomy])) {
247 $meta_a = intval(get_term_meta($a->term_id, $taxonomy_order_meta[$a->taxonomy], true));
248 $meta_b = intval(get_term_meta($b->term_id, $taxonomy_order_meta[$b->taxonomy], true));
249
250 return $meta_a - $meta_b;
251 }
252
253 return 0; // Default to no sorting if meta keys are not defined
254 }
255
256 /**
257 * Sorting function for sorting term data.
258 *
259 * @param array $a The first array to compare.
260 * @param array $b The second array to compare.
261 *
262 * @return int Returns an integer less than, equal to, or greater than zero if the first array is considered
263 * to be respectively less than, equal to, or greater than the second.
264 */
265 public function sort_terms($a, $b) {
266 // Compare parent values
267 $parentComparison = strcmp($a[7], $b[7]);
268
269 // If parent values are equal, compare Term ID values
270 return ($parentComparison === 0) ? strcmp($a[2], $b[2]) : $parentComparison;
271 }
272
273 private function get_terms_csv_data( $post_ids ) {
274 $csv_data_terms = [];
275
276 // Add CSV headers for terms
277 $csv_data_terms[] = [
278 'Type',
279 'Taxonomy',
280 'Term ID',
281 'Term name',
282 'Term slug',
283 'Term group',
284 'Term description',
285 'Term parent',
286 'Assigned Docs',
287 'Assigned KBs',
288 'Doc Category order',
289 'KB order', // Add additional term meta headers here
290 ];
291
292 if( $this->args['content'] == 'glossaries' ) {
293 $terms = get_terms([
294 'taxonomy' => 'glossaries',
295 'hide_empty' => false,
296 ]);
297 } else{
298 $terms = $this->get_terms( $post_ids, $this->args['include_faq'] );
299 }
300 foreach ( $terms as $term ) {
301 $term_meta = '';
302
303 // Add term meta based on taxonomy
304 switch ($term->taxonomy) {
305 case 'doc_category':
306 $doc_category_knowledge_base = maybe_unserialize(get_term_meta($term->term_id, 'doc_category_knowledge_base', true));
307 if (is_array($doc_category_knowledge_base) && $doc_category_knowledge_base !== false) {
308 $doc_category_knowledge_base = implode(', ', array_filter($doc_category_knowledge_base));
309 } else {
310 $doc_category_knowledge_base = '';
311 }
312
313 $term_meta = [
314 '_docs_order' => get_term_meta($term->term_id, '_docs_order', true),
315 'doc_category_knowledge_base' => $doc_category_knowledge_base,
316 'doc_category_order' => get_term_meta($term->term_id, 'doc_category_order', true),
317 ];
318 break;
319
320 case 'knowledge_base':
321 $term_meta = [
322 'kb_order' => get_term_meta($term->term_id, 'kb_order', true),
323 ];
324 break;
325 }
326
327 $parent = $term->parent ? get_term_by('id', $term->parent, $term->taxonomy) : '';
328 // Add CSV row for term
329 $csv_data_terms[] = [
330 'Term',
331 $term->taxonomy,
332 $term->term_id,
333 $term->name,
334 $term->slug,
335 $term->term_group,
336 $term->description,
337 $parent ? $parent->slug : '',
338 isset($term_meta['_docs_order']) ? $term_meta['_docs_order'] : '',
339 isset($term_meta['doc_category_knowledge_base']) ? $term_meta['doc_category_knowledge_base'] : '',
340 isset($term_meta['doc_category_order']) ? $term_meta['doc_category_order'] : '',
341 isset($term_meta['kb_order']) ? $term_meta['kb_order'] : '',
342 ];
343 }
344
345 return $csv_data_terms;
346 }
347
348 /**
349 * Return list of authors with posts.
350 *
351 * @param int[] $post_ids Optional. Array of post IDs to filter the query by.
352 *
353 * @return string
354 */
355 private function authors_list( $post_ids ) {
356 $authors = [];
357
358 // Add CSV headers for terms
359 $authors[] = [
360 'Type',
361 'Author id',
362 'Author login',
363 'Author email',
364 'Author display name',
365 'Author first name',
366 'Author last name'
367 ];
368
369 if ( ! empty( $post_ids ) ) {
370 $post_ids = array_map( 'absint', $post_ids );
371 $and = 'AND ID IN ( ' . implode( ', ', $post_ids ) . ')';
372 } else {
373 $and = '';
374 }
375 $authors_data = [];
376 $results = $this->wpdb->get_results( "SELECT DISTINCT post_author FROM {$this->wpdb->posts} WHERE post_status != 'auto-draft' $and" );// phpcs:ignore
377 foreach ( (array) $results as $r ) {
378 $authors_data[] = get_userdata( $r->post_author );
379 }
380
381 $authors_data = array_filter( $authors_data );
382
383 foreach ( $authors_data as $author ) {
384 $authors[] = [
385 'Author',
386 $author->ID,
387 $author->user_login,
388 $author->user_email,
389 $author->display_name,
390 $author->first_name,
391 $author->last_name
392 ];
393 }
394
395 return $authors;
396 }
397
398 private function get_posts_csv_data($posts) {
399 $csv_data_posts = [];
400
401 // Add CSV headers for posts
402 $csv_data_posts[] = [
403 'Type',
404 'Docs ID',
405 'Docs author',
406 'Docs date',
407 'Docs date gmt',
408 'Docs title',
409 'Docs content',
410 'Docs excerpt',
411 'Docs status',
412 'Docs password',
413 'Docs slug',
414 'Docs modified date',
415 'Docs modified date gmt',
416 'Docs parent',
417 'Docs menu order',
418 'Docs mime type',
419 'Comment count',
420 'Doc Categories',
421 'Doc Tags',
422 'Knowledge Bases',
423 'Docs attachement url',
424 'Docs attachement ID'
425 ];
426
427 foreach ($posts as $post) {
428 $attachment_id = get_post_thumbnail_id($post->ID);
429 $attachment_url = get_the_post_thumbnail_url($post->ID);
430 // Add CSV row for post
431 $csv_data_posts[] = [
432 $post->post_type == 'betterdocs_faq' ? 'FAQ' : 'Docs',
433 $post->ID,
434 $post->post_author,
435 $post->post_date,
436 $post->post_date_gmt,
437 $post->post_title,
438 $post->post_content,
439 $post->post_excerpt,
440 $post->post_status,
441 $post->post_password,
442 $post->post_name,
443 $post->post_modified,
444 $post->post_modified_gmt,
445 $post->post_parent,
446 $post->menu_order,
447 $post->post_mime_type,
448 $post->comment_count,
449 $this->get_term_ids( $post->ID, 'doc_category' ),
450 $this->get_term_ids( $post->ID, 'doc_tag' ),
451 $this->get_term_ids( $post->ID, 'knowledge_base' ),
452 $attachment_url ? $attachment_url : '',
453 $attachment_id ? $attachment_id : ''
454 ];
455
456 }
457
458 return $csv_data_posts;
459 }
460
461 public function get_term_ids($post_id, $taxonomy) {
462 $terms = get_the_terms($post_id, $taxonomy);
463
464 if ($terms && !is_wp_error($terms)) {
465 $term_ids = wp_list_pluck($terms, 'term_id');
466 return implode(', ', $term_ids);
467 }
468
469 return '';
470 }
471
472 private function generate_csv(array $data): string {
473 ob_start();
474
475 $output = fopen('php://output', 'w');
476
477 // Add CSV rows
478 foreach ($data as $row) {
479 fputcsv($output, $row);
480 }
481
482 fclose($output);
483
484 return ob_get_clean();
485 }
486 }
487