PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 2.3.6
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v2.3.6
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 / class-betterdocs-faq.php

class-betterdocs-faq.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 2.3.6, at includes/class-betterdocs-faq.php

616 lines 21.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Register docs post type and taxonomies.
5 *
6 * @link https://wpdeveloper.com
7 * @since 1.0.0
8 *
9 * @package BetterDocs
10 * @subpackage BetterDocs/includes
11 */
12
13 /**
14 * Register docs post type and taxonomies class.
15 *
16 *
17 * Also maintains the unique identifier of this plugin as well as the current
18 * version of the plugin.
19 *
20 * @since 1.0.0
21 * @package BetterDocs
22 * @subpackage BetterDocs/includes
23 * @author WPDeveloper <support@wpdeveloper.com>
24 */
25 class BetterDocs_FAQ
26 {
27 /**
28 * REST API namespace
29 * @var string
30 */
31 private $namespace = 'betterdocs';
32 public $post_type = 'betterdocs_faq';
33 public $category = 'betterdocs_faq_category';
34
35 /**
36 *
37 * Initialize the class and start calling our hooks and filters
38 *
39 * @since 1.0.0
40 *
41 */
42 public function __construct() {
43 // assign default admin capabilities for docs, doc terms, doc tags, knowledge base
44 add_action('init', array($this, 'register_post'));
45 // fires after a new betterdocs_faq_category is created
46 add_action('created_betterdocs_faq_category', array($this, 'action_created_betterdocs_faq_category'), 10, 2);
47 add_action('rest_api_init', array($this, 'register_api_endpoint'));
48 }
49
50 /**
51 *
52 * Register post type and taxonomies
53 *
54 * @since 1.0.0
55 *
56 */
57 public function register_post()
58 {
59 /**
60 * Register category taxonomy
61 */
62 $category_labels = array(
63 'name' => esc_html__('FAQ Categories', 'betterdocs'),
64 'singular_name' => esc_html__('FAQ Category', 'betterdocs'),
65 'all_items' => esc_html__('FAQ Categories', 'betterdocs'),
66 'parent_item' => esc_html__('Parent FAQ Category', 'betterdocs'),
67 'parent_item_colon' => esc_html__('Parent FAQ Category:', 'betterdocs'),
68 'edit_item' => esc_html__('Edit Category', 'betterdocs'),
69 'update_item' => esc_html__('Update Category', 'betterdocs'),
70 'add_new_item' => esc_html__('Add New FAQ Category', 'betterdocs'),
71 'new_item_name' => esc_html__('New FAQ Category Name', 'betterdocs'),
72 'menu_name' => esc_html__('Categories', 'betterdocs')
73 );
74
75 $category_args = array(
76 'hierarchical' => true,
77 'public' => false,
78 'labels' => $category_labels,
79 'show_ui' => true,
80 'show_admin_column' => true,
81 'query_var' => true,
82 'show_in_rest' => true,
83 'has_archive' => false,
84 'rewrite' => false,
85 'capabilities' => [
86 'manage_terms' => 'manage_doc_terms',
87 'edit_terms' => 'edit_doc_terms',
88 'delete_terms' => 'delete_doc_terms',
89 'assign_terms' => 'edit_docs'
90 ]
91 );
92
93 register_taxonomy($this->category, array($this->post_type), $category_args);
94 register_term_meta($this->category, 'order', ['show_in_rest' => true]);
95 register_term_meta($this->category, 'status', ['show_in_rest' => true]);
96 register_term_meta($this->category, '_betterdocs_faq_order', ['show_in_rest' => true]);
97
98 /**
99 * Register post type
100 */
101 $labels = array(
102 'name' => esc_html__('BetterDocs FAQ', 'betterdocs'),
103 'singular_name' => esc_html__('BetterDocs FAQ', 'betterdocs'),
104 'menu_name' => esc_html__('FAQ', 'betterdocs'),
105 'name_admin_bar' => esc_html__('FAQ', 'betterdocs'),
106 'add_new' => esc_html__('Add New', 'betterdocs'),
107 'add_new_item' => esc_html__('Add New FAQ', 'betterdocs'),
108 'new_item' => esc_html__('New FAQ', 'betterdocs'),
109 'edit_item' => esc_html__('Edit FAQ', 'betterdocs'),
110 'view_item' => esc_html__('View FAQ', 'betterdocs'),
111 'all_items' => esc_html__('All FAQ', 'betterdocs'),
112 'search_items' => esc_html__('Search FAQ', 'betterdocs'),
113 'parent_item_colorn' => null,
114 'not_found' => esc_html__('No FAQ found', 'betterdocs'),
115 'not_found_in_trash' => esc_html__('No FAQ found in trash', 'betterdocs')
116 );
117
118 $betterdocs_articles_caps = apply_filters('betterdocs_articles_caps', 'edit_posts', 'article_roles');
119
120 $args = array(
121 'labels' => $labels,
122 'description' => esc_html__('Add new faq from here', 'betterdocs'),
123 'public' => false,
124 'public_queryable' => true,
125 'exclude_from_search' => false,
126 'show_ui' => true,
127 'show_in_menu' => false,
128 //'show_in_admin_bar' => $betterdocs_articles_caps,
129 'query_var' => true,
130 'capability_type' => ['doc', 'docs'],
131 'hierarchical' => true,
132 'map_meta_cap' => true,
133 'has_archive' => false,
134 'rewrite' => false,
135 'show_in_rest' => true,
136 'menu_icon' => BETTERDOCS_ADMIN_URL . '/assets/img/betterdocs-icon-white.svg', 100,
137 'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'author', 'revisions', 'custom-fields', 'comments')
138 );
139
140 register_post_type($this->post_type, $args);
141
142 }
143
144
145 /**
146 * Default the taxonomy's terms' order if it's not set.
147 *
148 * @param string $tax_slug The taxonomy's slug.
149 */
150 public function action_created_betterdocs_faq_category( $term_id )
151 {
152 $order = $this->get_max_taxonomy_order('betterdocs_faq_category');
153 update_term_meta($term_id, 'order', $order++);
154 update_term_meta($term_id, 'status', 1);
155 }
156
157 /**
158 * Default the taxonomy's terms' order if it's not set.
159 *
160 * @param string $tax_slug The taxonomy's slug.
161 */
162 public function default_term_order($tax_slug)
163 {
164 $terms = get_terms($tax_slug, array('hide_empty' => false));
165 $order = $this->get_max_taxonomy_order($tax_slug);
166
167 foreach ($terms as $term) {
168 if (!get_term_meta($term->term_id, 'order', true)) {
169 update_term_meta($term->term_id, 'order', $order);
170 $order++;
171 }
172 }
173 }
174
175 /**
176 * Get the maximum order for this taxonomy. This will be applied to terms that don't have a tax position.
177 */
178 private function get_max_taxonomy_order( $tax_slug )
179 {
180 global $wpdb;
181
182 $max_term_order = $wpdb->get_col(
183 $wpdb->prepare(
184 "SELECT MAX( CAST( tm.meta_value AS UNSIGNED ) )
185 FROM $wpdb->terms t
186 JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id AND tt.taxonomy = '%s'
187 JOIN $wpdb->termmeta tm ON tm.term_id = t.term_id WHERE tm.meta_key = 'order'",
188 $tax_slug
189 )
190 );
191
192 $max_term_order = is_array($max_term_order) ? current($max_term_order) : 0;
193
194 return (int) $max_term_order === 0 || empty($max_term_order) ? 1 : (int) $max_term_order + 1;
195 }
196
197 /**
198 * Re-Order the taxonomies based on the order value.
199 *
200 * @param array $pieces Array of SQL query clauses.
201 * @param array $taxonomies Array of taxonomy names.
202 * @param array $args Array of term query args.
203 */
204 public function set_tax_order($pieces, $taxonomies, $args)
205 {
206 foreach ($taxonomies as $taxonomy) {
207 global $wpdb;
208
209 if ($taxonomy === 'betterdocs_faq_category') {
210 $join_statement = " LEFT JOIN $wpdb->termmeta AS term_meta ON t.term_id = term_meta.term_id AND term_meta.meta_key = 'order'";
211
212 if (!$this->does_substring_exist($pieces['join'], $join_statement)) {
213 $pieces['join'] .= $join_statement;
214 }
215
216 $pieces['orderby'] = 'ORDER BY CAST( term_meta.meta_value AS UNSIGNED )';
217 }
218 }
219
220 return $pieces;
221 }
222
223 /**
224 * Order the taxonomies on the front end.
225 */
226 public function front_end_order_terms()
227 {
228 if (!is_admin()) {
229 add_filter('terms_clauses', array($this, 'set_tax_order'), 10, 3);
230 }
231 }
232
233 /**
234 * Check if a substring exists inside a string.
235 *
236 * @param string $string The main string (haystack) we're searching in.
237 * @param string $substring The substring we're searching for.
238 *
239 * @return bool True if substring exists, else false.
240 */
241 protected function does_substring_exist($string, $substring)
242 {
243 return strstr($string, $substring) !== false;
244 }
245
246
247 public function register_api_endpoint() {
248 register_rest_route( $this->namespace, '/faq/sample_data', array(
249 'methods' => [ 'POST' ],
250 'callback' => array( $this, 'create_faq_sample' ),
251 'permission_callback' => function() {
252 return current_user_can('edit_others_posts');
253 }
254 ));
255
256 register_rest_route( $this->namespace, '/faq/posts/(?P<type>\S+)', array(
257 'methods' => [ 'GET' ],
258 'callback' => array( $this, 'fetch_faq_posts' ),
259 'permission_callback' => '__return_true'
260 ));
261
262 register_rest_route( $this->namespace, '/faq/create_category', array(
263 'methods' => [ 'POST' ],
264 'callback' => array( $this, 'create_faq_category' ),
265 'permission_callback' => function() {
266 return current_user_can('edit_others_posts');
267 }
268 ));
269
270 register_rest_route( $this->namespace, '/faq/update_category', array(
271 'methods' => [ 'POST' ],
272 'callback' => array( $this, 'update_faq_category' ),
273 'permission_callback' => function() {
274 return current_user_can('edit_others_posts');
275 }
276 ));
277
278 register_rest_route( $this->namespace, '/faq/delete_category', array(
279 'methods' => [ 'POST' ],
280 'callback' => array( $this, 'delete_faq_category' ),
281 'permission_callback' => function() {
282 return current_user_can('edit_others_posts');
283 }
284 ));
285
286 register_rest_route( $this->namespace, '/faq/create_post', array(
287 'methods' => [ 'POST' ],
288 'callback' => array( $this, 'create_betterdocs_faq' ),
289 'permission_callback' => function() {
290 return current_user_can('edit_others_posts');
291 }
292 ));
293
294 register_rest_route( $this->namespace, '/faq/update_post', array(
295 'methods' => [ 'POST' ],
296 'callback' => array( $this, 'update_betterdocs_faq' ),
297 'permission_callback' => function() {
298 return current_user_can('edit_others_posts');
299 }
300 ));
301
302 register_rest_route( $this->namespace, '/faq/delete_post', array(
303 'methods' => [ 'POST' ],
304 'callback' => array( $this, 'delete_betterdocs_faq' ),
305 'permission_callback' => function() {
306 return current_user_can('edit_others_posts');
307 }
308 ));
309
310 register_rest_route( $this->namespace, '/faq/category_status', array(
311 'methods' => [ 'POST' ],
312 'callback' => array( $this, 'update_category_status' ),
313 'permission_callback' => function() {
314 return current_user_can('edit_others_posts');
315 }
316 ));
317
318 register_rest_route( $this->namespace, '/faq/category_order', array(
319 'methods' => [ 'POST' ],
320 'callback' => array( $this, 'update_faq_category_order' ),
321 'permission_callback' => function() {
322 return current_user_can('edit_others_posts');
323 }
324 ));
325
326 register_rest_route( $this->namespace, '/faq/update_order_by_category', array(
327 'methods' => [ 'POST' ],
328 'callback' => array( $this, 'update_faq_order_by_category' ),
329 'permission_callback' => function() {
330 return current_user_can('edit_others_posts');
331 }
332 ));
333
334 register_rest_route( $this->namespace, '/faq/uncategorised', array(
335 'methods' => [ 'GET' ],
336 'callback' => array( $this, 'get_uncategorised_faq' ),
337 'permission_callback' => '__return_true'
338 ));
339 }
340
341 public function create_faq_sample( $params ) {
342 $sample_data = json_decode($params->get_param('sample_data'), true);
343 foreach ( $sample_data as $key => $value ) {
344 $insert_term = wp_insert_term(
345 $key,
346 'betterdocs_faq_category'
347 );
348 if ( $insert_term ) {
349 foreach ( $value['posts'] as $key => $value) {
350 $this->insert_betterdocs_faq($value['post_title'], $value['post_content'], $insert_term['term_id']);
351 }
352 }
353 }
354 return true;
355 }
356
357 public function create_faq_category( $params ) {
358 $title = $params->get_param('title');
359 $description = $params->get_param('description');
360 $description = ($description !== 'undefined') ? $description : '';
361 $slug = $params->get_param('slug');
362 return $this->insert_betterdocs_faq_category( $title, $description, $slug );
363 }
364
365 public function update_faq_category( $params ) {
366 $term_id = $params->get_param('term_id');
367 $title = $params->get_param('title');
368 $description = $params->get_param('description');
369 $description = ($description !== 'undefined') ? $description : '';
370 $slug = $params->get_param('slug');
371 $update = wp_update_term( $term_id, 'betterdocs_faq_category', array(
372 'name' => $title,
373 'slug' => $slug,
374 'description' => $description
375 ) );
376
377 if ( is_wp_error( $update ) ) {
378 return $update;
379 } else {
380 return true;
381 }
382 }
383
384 public function delete_faq_category( $params ) {
385 $term_id = $params->get_param('term_id');
386 $delete = wp_delete_term( $term_id, 'betterdocs_faq_category' );
387
388 if( is_wp_error( $delete ) ) {
389 return $delete;
390 } else {
391 return true;
392 }
393 }
394
395 public function insert_betterdocs_faq_category( $title, $description, $slug='' ) {
396 $insert_term = wp_insert_term(
397 $title,
398 'betterdocs_faq_category',
399 array(
400 'slug' => $slug,
401 'description' => $description
402 )
403 );
404
405 if ( is_wp_error( $insert_term ) ) {
406 return $insert_term;
407 } else {
408 return true;
409 }
410 }
411
412 public function update_faq_category_order($params)
413 {
414 $faq_category_order = $params->get_param('faq_category_order');
415 $faq_category_order = json_decode($faq_category_order, true);
416
417 foreach ($faq_category_order as $order_data) {
418 if ((int) $order_data['current_position'] != (int) $order_data['updated_position']) {
419 update_term_meta($order_data['id'], 'order', ((int) $order_data['updated_position']));
420 }
421 }
422 return true;
423 }
424
425 public function insert_betterdocs_faq( $post_title, $post_content, $term_id ) {
426 $post = wp_insert_post(
427 array(
428 'post_type' => 'betterdocs_faq',
429 'post_title' => wp_strip_all_tags( $post_title ),
430 'post_content' => $post_content,
431 'post_status' => 'publish'
432 )
433 );
434
435 if ( $term_id ) {
436 $set_terms = wp_set_object_terms( $post, $term_id, 'betterdocs_faq_category' );
437 if ( is_wp_error( $set_terms ) ) {
438 return $set_terms;
439 } else {
440 return $this->update_faq_order_on_insert($term_id, $post);
441 }
442 } else {
443 return $post;
444 }
445 }
446
447 public function update_faq_order_on_insert( $term_id, $post )
448 {
449 $term_meta = get_term_meta( $term_id, '_betterdocs_faq_order' );
450 if ( ! empty( $term_meta ) ) {
451 $term_meta_arr = explode(",", $term_meta[0]);
452 if ( ! in_array( $post, $term_meta_arr ) ) {
453 array_unshift($term_meta_arr, $post);
454 $docs_ordering_data = filter_var_array(wp_unslash($term_meta_arr), FILTER_SANITIZE_NUMBER_INT);
455 return update_term_meta($term_id, '_betterdocs_faq_order', implode(',', $docs_ordering_data));
456 }
457 } else {
458 return update_term_meta($term_id, '_betterdocs_faq_order', $post);
459 }
460 }
461
462 /**
463 * Update _betterdocs_faq_order meta when new post created
464 */
465
466 public function update_faq_order_by_category( $params )
467 {
468 $term_id = $params->get_param('term_id');
469 $posts = $params->get_param('posts');
470 return update_term_meta($term_id, '_betterdocs_faq_order', $posts);
471 }
472
473 public function create_betterdocs_faq( $params ) {
474 $post_title = $params->get_param('post_title');
475 $post_content = $params->get_param('post_content');
476 $term_id = $params->get_param('term_id');
477 return $this->insert_betterdocs_faq( $post_title, $post_content, $term_id );
478 }
479
480 public function update_betterdocs_faq( $params ) {
481 $post_id = $params->get_param('post_id');
482 $post_title = $params->get_param('post_title');
483 $post_content = $params->get_param('post_content');
484 $status = $params->get_param('status');
485 $term_id = $params->get_param('term_id');
486 if ($status) {
487 $data = array(
488 'post_type' => 'betterdocs_faq',
489 'ID' => $post_id,
490 'status' => $status
491 );
492 } else {
493 $data = array(
494 'post_type' => 'betterdocs_faq',
495 'ID' => $post_id,
496 'post_title' => $post_title,
497 'post_content' => $post_content
498 );
499
500 if ( $term_id ) {
501 $data['tax_input'] = array(
502 "betterdocs_faq_category" => $term_id
503 );
504
505 $term_meta = get_term_meta( $term_id, '_betterdocs_faq_order' );
506 $term_meta_arr = explode(",", $term_meta[0]);
507 if ( ! in_array( $post_id, $term_meta_arr ) ) {
508 array_unshift($term_meta_arr, $post_id);
509 $docs_ordering_data = filter_var_array(wp_unslash($term_meta_arr), FILTER_SANITIZE_NUMBER_INT);
510 update_term_meta($term_id, '_betterdocs_faq_order', implode(',', $docs_ordering_data));
511 }
512 }
513 }
514
515 return wp_update_post( $data );
516 }
517
518 public function delete_betterdocs_faq( $params ) {
519 $post_id = $params->get_param('post_id');
520 return wp_delete_post($post_id);
521 }
522
523 public function faq_post_loop($args) {
524 $posts = [];
525 $query = new WP_Query( $args );
526 if ( $query->have_posts() ) :
527 while ($query->have_posts()) : $query->the_post();
528 $posts[get_the_ID()]['title'] = get_the_title();
529 $posts[get_the_ID()]['content'] = get_the_content();
530 endwhile;
531 endif;
532
533 return $posts;
534 }
535
536 public function update_category_status( $params ) {
537 $term_id = $params->get_param('term_id');
538 $status = $params->get_param('status');
539 return update_term_meta($term_id, 'status', $status);
540 }
541
542 public function fetch_faq_posts( $params ) {
543 $faq = [];
544 $type = $params->get_param('type');
545
546 if ( $type == 'category' ) {
547 $taxonomy_objects = get_terms( 'betterdocs_faq_category', array(
548 'hide_empty' => false,
549 ) );
550
551 if ( $taxonomy_objects && !is_wp_error( $taxonomy_objects ) ) :
552 foreach ( $taxonomy_objects as $term ) :
553 $args = array(
554 'post_type' => 'betterdocs_faq',
555 'post_status' => 'publish',
556 'post_per_page' => -1,
557 'tax_query' => array(
558 array(
559 'taxonomy' => 'betterdocs_faq_category',
560 'field' => 'term_id',
561 'terms' => $term->term_id
562 )
563 )
564 );
565
566 $posts = $this->faq_post_loop($args);
567
568 $faq[$term->slug] = [
569 (array) $term,
570 'posts' => $posts
571 ];
572 endforeach;
573 endif;
574 } else {
575 $args = array(
576 'post_type' => 'betterdocs_faq',
577 'post_status' => 'publish',
578 'post_per_page' => -1,
579 );
580 $posts = $this->faq_post_loop($args);
581 $faq['posts'] = $posts;
582 }
583
584 return $faq;
585 }
586
587 public function get_uncategorised_faq() {
588 $available_terms = array_map( function ( $term ) {
589 return $term->term_id;
590 }, get_terms( array(
591 'taxonomy' => 'betterdocs_faq_category',
592 'hide_empty' => false,
593 ) ) );
594
595 return get_posts( [
596 'post_type' => 'betterdocs_faq',
597 'post_status' => array('publish', 'draft'),
598 'posts_per_page' => -1,
599 'tax_query' => [
600 'relation' => 'AND',
601 [
602 'taxonomy' => 'betterdocs_faq_category',
603 'field' => 'term_id',
604 'terms' => $available_terms,
605 'operator' => 'NOT IN'
606 ]
607 ]
608 ] );
609 }
610
611 }
612 if (class_exists('BetterDocs_FAQ')) {
613 new BetterDocs_FAQ;
614 }
615
616